"use client";

import { useEffect, useMemo, useRef, useState } from "react";
import {
  createChart,
  type CandlestickData,
  type IChartApi,
  type ISeriesApi,
  type LineData,
  type UTCTimestamp,
} from "lightweight-charts";
import type { ChartPayload, LatestPredictionResponse } from "../lib/api";

type Props = {
  chart: ChartPayload;
  prediction?: LatestPredictionResponse | null;
};

// KST naive ISO ('YYYY-MM-DD' 또는 'YYYY-MM-DDTHH:MM:SS') 를 UTCTimestamp 로.
// lightweight-charts 는 UTC 라고 가정하지만 KST wall-clock 을 UTC 처럼 넣어
// timeScale 표시가 한국 사용자에게 자연스럽게 KST 그대로 나오도록 함.
function isoToUtcTs(s: string): UTCTimestamp {
  if (s.length <= 10) {
    return (Date.UTC(
      Number(s.slice(0, 4)),
      Number(s.slice(5, 7)) - 1,
      Number(s.slice(8, 10)),
    ) / 1000) as UTCTimestamp;
  }
  return (Date.UTC(
    Number(s.slice(0, 4)),
    Number(s.slice(5, 7)) - 1,
    Number(s.slice(8, 10)),
    Number(s.slice(11, 13)),
    Number(s.slice(14, 16)),
    Number(s.slice(17, 19) || "0"),
  ) / 1000) as UTCTimestamp;
}

// 한국 거래소 관행: 상승=빨강, 하락=파랑.
const COLOR_UP = "#ef4444";
const COLOR_DOWN = "#3b82f6";
// 예측 캔들은 같은 up/down 색 옅은 톤. "어우러지되 미래 데이터임이 명확" 한 시각 신호.
const PRED_UP = "#fda4af";
const PRED_DOWN = "#93c5fd";

// 단순 이동평균. 윈도우 미만은 null (lightweight-charts 는 whitespace 로 처리됨).
function sma(values: (number | null)[], window: number): (number | null)[] {
  const out: (number | null)[] = new Array(values.length).fill(null);
  let sum = 0;
  let count = 0;
  const buf: (number | null)[] = [];
  for (let i = 0; i < values.length; i++) {
    const v = values[i];
    buf.push(v);
    if (v != null) {
      sum += v;
      count += 1;
    }
    if (buf.length > window) {
      const drop = buf.shift();
      if (drop != null) {
        sum -= drop;
        count -= 1;
      }
    }
    if (buf.length === window && count === window) {
      out[i] = sum / window;
    }
  }
  return out;
}

type SmaWindow = 5 | 20 | 60;

const SMA_PRESETS: { window: SmaWindow; color: string; label: string }[] = [
  { window: 5, color: "#f59e0b", label: "MA5" },
  { window: 20, color: "#22d3ee", label: "MA20" },
  { window: 60, color: "#a78bfa", label: "MA60" },
];

export function StockChart({ chart, prediction }: Props) {
  const containerRef = useRef<HTMLDivElement | null>(null);
  const chartRef = useRef<IChartApi | null>(null);
  const candleRef = useRef<ISeriesApi<"Candlestick"> | null>(null);
  const predRef = useRef<ISeriesApi<"Candlestick"> | null>(null);
  const smaRefs = useRef<Map<SmaWindow, ISeriesApi<"Line">>>(new Map());

  // 토글 상태 — 기본 MA20 만 ON. 사용자 컨트롤이 너무 시끄럽지 않게.
  const [active, setActive] = useState<Set<SmaWindow>>(() => new Set<SmaWindow>([20]));

  const isIntraday = chart.interval === "10m";
  const showSma = !isIntraday; // 10분봉에선 의미 적음 — UI 도 숨김.

  // chart 생성 — interval 바뀌면 timeVisible 토글을 위해 재생성.
  useEffect(() => {
    if (!containerRef.current) return;
    const c = createChart(containerRef.current, {
      layout: {
        background: { color: "transparent" },
        textColor: "#cbd5e1",
      },
      grid: {
        vertLines: { color: "#1f2937" },
        horzLines: { color: "#1f2937" },
      },
      rightPriceScale: { borderColor: "#374151" },
      timeScale: {
        borderColor: "#374151",
        timeVisible: isIntraday,
        secondsVisible: false,
      },
      autoSize: true,
      crosshair: { mode: 1 },
    });
    const candle = c.addCandlestickSeries({
      upColor: COLOR_UP,
      downColor: COLOR_DOWN,
      borderUpColor: COLOR_UP,
      borderDownColor: COLOR_DOWN,
      wickUpColor: COLOR_UP,
      wickDownColor: COLOR_DOWN,
    });
    chartRef.current = c;
    candleRef.current = candle;
    const smaMap = smaRefs.current;
    return () => {
      c.remove();
      chartRef.current = null;
      candleRef.current = null;
      predRef.current = null;
      smaMap.clear();
    };
  }, [isIntraday]);

  // 실 데이터 push.
  useEffect(() => {
    if (!candleRef.current) return;
    const data: CandlestickData[] = chart.ohlcv
      .filter((p) => p.open !== null && p.high !== null && p.low !== null && p.close !== null)
      .map((p) => ({
        time: isoToUtcTs(p.date),
        open: p.open as number,
        high: p.high as number,
        low: p.low as number,
        close: p.close as number,
      }));
    candleRef.current.setData(data);
    chartRef.current?.timeScale().fitContent();
  }, [chart, isIntraday]);

  // SMA 시리즈 — chart.ohlcv 종가 기준. 토글 변화 또는 데이터 변화 시 갱신.
  // 비활성 윈도우는 제거하고, 활성은 재계산. 마지막에 fitContent 는 호출 안 함 — 사용자 줌 유지.
  const closes = useMemo(() => chart.ohlcv.map((p) => p.close), [chart]);
  useEffect(() => {
    const c = chartRef.current;
    if (!c) return;
    if (!showSma) {
      // intraday: 다 지움.
      for (const s of smaRefs.current.values()) c.removeSeries(s);
      smaRefs.current.clear();
      return;
    }
    // 비활성 윈도우 제거.
    for (const [w, s] of smaRefs.current) {
      if (!active.has(w)) {
        c.removeSeries(s);
        smaRefs.current.delete(w);
      }
    }
    // 활성 윈도우 생성/갱신.
    for (const preset of SMA_PRESETS) {
      if (!active.has(preset.window)) continue;
      const values = sma(closes, preset.window);
      const lineData: LineData[] = [];
      for (let i = 0; i < values.length; i++) {
        const v = values[i];
        if (v == null) continue;
        lineData.push({ time: isoToUtcTs(chart.ohlcv[i].date), value: v });
      }
      let s = smaRefs.current.get(preset.window);
      if (!s) {
        s = c.addLineSeries({
          color: preset.color,
          lineWidth: 1,
          priceLineVisible: false,
          lastValueVisible: false,
          title: preset.label,
        });
        smaRefs.current.set(preset.window, s);
      }
      s.setData(lineData);
    }
  }, [active, closes, chart, showSma]);

  // 예측 오버레이를 캔들 시리즈로 렌더.
  // 합성 OHLC:
  //   open[i]  = i==0 ? base_close : prev.point_close
  //   close[i] = point_close
  //   high[i]  = max(ci_high, open, close)
  //   low[i]   = min(ci_low,  open, close)
  // → 몸통 = 기간 사이 예상 변동, 위/아래 꼬리 = 신뢰구간 폭 (불확실성).
  // 10분봉(intraday) 에서는 예측 표시 안 함 — 모델은 일봉 기반.
  useEffect(() => {
    if (!chartRef.current) return;
    if (predRef.current) {
      chartRef.current.removeSeries(predRef.current);
      predRef.current = null;
    }
    if (isIntraday) return;
    if (!prediction?.found || !prediction.steps?.length) return;
    const baseDate = prediction.base_date;
    const baseClose = prediction.base_close;
    if (!baseDate || baseClose == null) return;

    const sorted = [...prediction.steps]
      .filter(
        (s) =>
          s.point_close != null && s.ci_low != null && s.ci_high != null && s.target_date,
      )
      .sort((a, b) => a.horizon - b.horizon);
    if (!sorted.length) return;

    const data: CandlestickData[] = [];
    let prevClose = baseClose;
    for (const s of sorted) {
      const open = prevClose;
      const close = s.point_close as number;
      const ciHi = s.ci_high as number;
      const ciLo = s.ci_low as number;
      data.push({
        time: isoToUtcTs(s.target_date),
        open,
        close,
        high: Math.max(ciHi, open, close),
        low: Math.min(ciLo, open, close),
      });
      prevClose = close;
    }

    const pred = chartRef.current.addCandlestickSeries({
      upColor: PRED_UP,
      downColor: PRED_DOWN,
      borderUpColor: PRED_UP,
      borderDownColor: PRED_DOWN,
      wickUpColor: PRED_UP,
      wickDownColor: PRED_DOWN,
      priceLineVisible: false,
      lastValueVisible: true,
      title: "예측",
    });
    pred.setData(data);
    predRef.current = pred;
    chartRef.current.timeScale().fitContent();
  }, [prediction, isIntraday]);

  const toggle = (w: SmaWindow) => {
    setActive((cur) => {
      const next = new Set(cur);
      if (next.has(w)) next.delete(w);
      else next.add(w);
      return next;
    });
  };

  return (
    <div className="w-full rounded-lg border border-zinc-800 bg-zinc-900/30 p-2">
      {showSma && (
        <div className="mb-1 flex items-center gap-1.5 px-1">
          {SMA_PRESETS.map((p) => {
            const on = active.has(p.window);
            return (
              <button
                key={p.window}
                type="button"
                onClick={() => toggle(p.window)}
                className={`flex items-center gap-1 rounded-full border px-2 py-0.5 text-[10px] transition ${
                  on
                    ? "border-zinc-600 bg-zinc-800/80 text-zinc-100"
                    : "border-zinc-800 bg-transparent text-zinc-500 hover:text-zinc-300"
                }`}
                aria-pressed={on}
              >
                <span
                  className="inline-block h-2 w-2 rounded-full"
                  style={{ backgroundColor: on ? p.color : "transparent", border: `1px solid ${p.color}` }}
                />
                {p.label}
              </button>
            );
          })}
        </div>
      )}
      <div className="h-[460px] w-full">
        <div ref={containerRef} className="h-full w-full" />
      </div>
      {prediction?.found && !isIntraday && (
        <div className="mt-1 flex items-center justify-end gap-2 px-2 text-[11px] text-zinc-500">
          <span className="inline-block h-2 w-2 rounded-sm bg-rose-300" />
          <span className="inline-block h-2 w-2 rounded-sm bg-sky-300" />
          <span>예측 (꼬리 = q10~q90 신뢰구간)</span>
        </div>
      )}
    </div>
  );
}
