"use client";

// /compare?codes=005930,000660,...
// 최대 5 종목까지 같은 기간의 누적수익률(%)을 한 차트에 겹쳐서 비교.
// 토스의 "종목 비교" 화면 톤. 각 종목 첫 종가를 기준선(0%)으로 정규화.
//
// 구현:
//   - URLSearchParams 의 codes (콤마구분) ↔ 내부 state 양방향
//   - 기간 토글: 1M/3M/6M/1Y (각각 21/63/126/252 거래일)
//   - 종목별 /api/chart 병렬 fetch → 누적수익률로 매핑 → lightweight-charts line 시리즈
//   - 종목 추가는 헤더의 작은 검색바 (debounced)
//   - 색상 팔레트 5색 순환

import Link from "next/link";
import { useRouter, useSearchParams } from "next/navigation";
import { Suspense, useEffect, useMemo, useRef, useState } from "react";
import {
  createChart,
  type IChartApi,
  type ISeriesApi,
  type LineData,
  type UTCTimestamp,
} from "lightweight-charts";
import { api, type Symbol as SymbolItem } from "../../lib/api";

const MAX_CODES = 5;
const PALETTE = ["#f43f5e", "#22d3ee", "#a78bfa", "#facc15", "#34d399"];

type Range = "1M" | "3M" | "6M" | "1Y";
const RANGE_DAYS: Record<Range, number> = { "1M": 21, "3M": 63, "6M": 126, "1Y": 252 };

type Series = {
  code: string;
  name: string;
  points: { time: UTCTimestamp; value: number }[];
  finalPct: number | null;
};

function isoToUtcTs(s: string): UTCTimestamp {
  return (Date.UTC(
    Number(s.slice(0, 4)),
    Number(s.slice(5, 7)) - 1,
    Number(s.slice(8, 10)),
  ) / 1000) as UTCTimestamp;
}

export default function ComparePage() {
  // useSearchParams 는 Suspense 안에서 안전. 내부 컴포넌트로 분리.
  return (
    <Suspense fallback={null}>
      <CompareInner />
    </Suspense>
  );
}

function CompareInner() {
  const router = useRouter();
  const search = useSearchParams();
  const codesParam = search.get("codes") ?? "";
  const [codes, setCodes] = useState<string[]>(() => parseCodes(codesParam));
  const [range, setRange] = useState<Range>("3M");
  const [series, setSeries] = useState<Series[]>([]);
  const [err, setErr] = useState<string | null>(null);

  // URL ↔ state 동기화 — 외부에서 codes= 가 바뀌면 흡수.
  useEffect(() => {
    setCodes(parseCodes(codesParam));
  }, [codesParam]);

  // codes 변경을 URL 에 반영.
  const writeUrl = (next: string[]) => {
    const q = next.length ? `?codes=${next.join(",")}` : "";
    router.replace(`/compare${q}`, { scroll: false });
  };

  const add = (code: string, _name?: string) => {
    if (!code) return;
    if (codes.includes(code)) return;
    if (codes.length >= MAX_CODES) return;
    const next = [...codes, code];
    setCodes(next);
    writeUrl(next);
  };
  const remove = (code: string) => {
    const next = codes.filter((c) => c !== code);
    setCodes(next);
    writeUrl(next);
  };

  // 시리즈 fetch.
  useEffect(() => {
    let alive = true;
    setErr(null);
    if (codes.length === 0) {
      setSeries([]);
      return;
    }
    const days = RANGE_DAYS[range] + 8; // 비거래일 여유.
    Promise.all(
      codes.map(async (code) => {
        const c = await api.getChart(code, days, "1d");
        const valid = c.ohlcv.filter((p) => p.close != null && p.close > 0);
        if (!valid.length) {
          return {
            code,
            name: c.name,
            points: [],
            finalPct: null,
          } as Series;
        }
        const base = valid[0].close as number;
        const pts = valid.map((p) => ({
          time: isoToUtcTs(p.date),
          value: (((p.close as number) - base) / base) * 100,
        }));
        const final = pts[pts.length - 1].value;
        return { code, name: c.name, points: pts, finalPct: final };
      }),
    )
      .then((r) => {
        if (alive) setSeries(r);
      })
      .catch((e) => {
        if (alive) setErr(e instanceof Error ? e.message : String(e));
      });
    return () => {
      alive = false;
    };
  }, [codes, range]);

  return (
    <main className="mx-auto max-w-5xl px-6 py-8">
      <div className="mb-4 flex items-center justify-between gap-3">
        <Link href="/" className="text-xs text-zinc-500 hover:text-zinc-300">
          ← 홈
        </Link>
        <span className="text-[11px] text-zinc-500">
          {codes.length} / {MAX_CODES} 종목
        </span>
      </div>

      <h1 className="text-2xl font-bold text-zinc-50">종목 비교</h1>
      <p className="mt-1 text-sm text-zinc-400">
        기준일 종가를 0% 로 정규화한 누적수익률. 최대 {MAX_CODES} 종목까지 겹쳐서 보여줍니다.
      </p>

      <div className="mt-5 flex flex-wrap items-center gap-2">
        <RangeTabs value={range} onChange={setRange} />
        <SearchAdd onPick={add} disabled={codes.length >= MAX_CODES} />
      </div>

      <div className="mt-4 flex flex-wrap gap-2">
        {series.map((s, i) => (
          <Chip
            key={s.code}
            color={PALETTE[i % PALETTE.length]}
            name={s.name}
            code={s.code}
            pct={s.finalPct}
            onRemove={() => remove(s.code)}
          />
        ))}
        {codes.length === 0 && (
          <span className="text-xs text-zinc-500">
            상단의 검색바에서 종목을 골라 추가하세요.
          </span>
        )}
      </div>

      {err && (
        <div className="mt-4 rounded-md border border-rose-500/30 bg-rose-500/5 p-3 text-xs text-rose-200">
          비교 데이터 로딩 실패: {err}
        </div>
      )}

      <div className="mt-4">
        <CompareChart series={series} />
      </div>
    </main>
  );
}

function parseCodes(raw: string): string[] {
  if (!raw) return [];
  return Array.from(
    new Set(
      raw
        .split(",")
        .map((s) => s.trim())
        .filter((s) => /^[0-9A-Za-z]{4,12}$/.test(s)),
    ),
  ).slice(0, MAX_CODES);
}

function RangeTabs({
  value,
  onChange,
}: {
  value: Range;
  onChange: (r: Range) => void;
}) {
  return (
    <div className="flex overflow-hidden rounded-md border border-zinc-700 text-[11px]">
      {(["1M", "3M", "6M", "1Y"] as Range[]).map((r) => (
        <button
          key={r}
          type="button"
          onClick={() => onChange(r)}
          className={`px-2.5 py-1 ${
            value === r
              ? "bg-zinc-800 text-zinc-100"
              : "bg-transparent text-zinc-500 hover:text-zinc-300"
          }`}
        >
          {r}
        </button>
      ))}
    </div>
  );
}

function SearchAdd({
  onPick,
  disabled,
}: {
  onPick: (code: string, name: string) => void;
  disabled: boolean;
}) {
  const [q, setQ] = useState("");
  const [items, setItems] = useState<SymbolItem[]>([]);
  const [open, setOpen] = useState(false);
  const wrapRef = useRef<HTMLDivElement | null>(null);

  useEffect(() => {
    const term = q.trim();
    if (!term) {
      setItems([]);
      return;
    }
    const h = setTimeout(async () => {
      try {
        const r = await api.search(term, false, 6, false);
        setItems(r.items);
      } catch {
        setItems([]);
      }
    }, 200);
    return () => clearTimeout(h);
  }, [q]);

  useEffect(() => {
    const onDoc = (e: MouseEvent) => {
      if (!wrapRef.current) return;
      if (!wrapRef.current.contains(e.target as Node)) setOpen(false);
    };
    document.addEventListener("mousedown", onDoc);
    return () => document.removeEventListener("mousedown", onDoc);
  }, []);

  return (
    <div ref={wrapRef} className="relative">
      <input
        value={q}
        onChange={(e) => {
          setQ(e.target.value);
          setOpen(true);
        }}
        onFocus={() => setOpen(true)}
        disabled={disabled}
        placeholder={disabled ? "최대치 도달" : "종목 추가 검색"}
        className="w-56 rounded-md border border-zinc-700 bg-zinc-900 px-2.5 py-1 text-xs text-zinc-100 outline-none focus:border-zinc-500 disabled:cursor-not-allowed disabled:opacity-50"
      />
      {open && q.trim() && items.length > 0 && (
        <ul className="absolute left-0 right-0 top-full z-10 mt-1 max-h-72 overflow-auto rounded-md border border-zinc-700 bg-zinc-950 shadow-xl">
          {items.map((it) => (
            <li key={it.code}>
              <button
                type="button"
                onClick={() => {
                  onPick(it.code, it.name);
                  setQ("");
                  setOpen(false);
                }}
                className="flex w-full items-center justify-between gap-2 px-2.5 py-1.5 text-left text-xs hover:bg-zinc-800/60"
              >
                <span className="min-w-0 truncate text-zinc-100">{it.name}</span>
                <span className="shrink-0 text-zinc-500">
                  {it.code} · {it.market}
                </span>
              </button>
            </li>
          ))}
        </ul>
      )}
    </div>
  );
}

function Chip({
  color,
  name,
  code,
  pct,
  onRemove,
}: {
  color: string;
  name: string;
  code: string;
  pct: number | null;
  onRemove: () => void;
}) {
  const tone = pct == null ? "text-zinc-400" : pct > 0 ? "text-rose-300" : pct < 0 ? "text-sky-300" : "text-zinc-300";
  return (
    <div className="flex items-center gap-2 rounded-full border border-zinc-700 bg-zinc-900/50 px-2.5 py-1 text-xs">
      <span className="inline-block h-2 w-2 rounded-full" style={{ backgroundColor: color }} />
      <span className="text-zinc-100">{name}</span>
      <span className="text-[10px] text-zinc-500">{code}</span>
      {pct != null && (
        <span className={`tabular-nums ${tone}`}>
          {pct >= 0 ? "+" : ""}
          {pct.toFixed(2)}%
        </span>
      )}
      <button
        type="button"
        onClick={onRemove}
        className="ml-1 text-zinc-500 hover:text-rose-300"
        aria-label="제거"
      >
        ✕
      </button>
    </div>
  );
}

function CompareChart({ series }: { series: Series[] }) {
  const containerRef = useRef<HTMLDivElement | null>(null);
  const chartRef = useRef<IChartApi | null>(null);
  const seriesRef = useRef<Map<string, ISeriesApi<"Line">>>(new Map());

  // chart 생성/파괴.
  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: false },
      autoSize: true,
      crosshair: { mode: 1 },
    });
    chartRef.current = c;
    const map = seriesRef.current;
    return () => {
      c.remove();
      chartRef.current = null;
      map.clear();
    };
  }, []);

  // 시리즈 동기화.
  useEffect(() => {
    const c = chartRef.current;
    if (!c) return;
    const wanted = new Set(series.map((s) => s.code));
    // 제거된 종목 unsubscribe.
    for (const [code, s] of seriesRef.current) {
      if (!wanted.has(code)) {
        c.removeSeries(s);
        seriesRef.current.delete(code);
      }
    }
    // 추가/갱신.
    series.forEach((s, i) => {
      let line = seriesRef.current.get(s.code);
      if (!line) {
        line = c.addLineSeries({
          color: PALETTE[i % PALETTE.length],
          lineWidth: 2,
          priceLineVisible: false,
          lastValueVisible: false,
          title: s.name,
        });
        seriesRef.current.set(s.code, line);
      } else {
        line.applyOptions({ color: PALETTE[i % PALETTE.length] });
      }
      const data: LineData[] = s.points.map((p) => ({ time: p.time, value: p.value }));
      line.setData(data);
    });
    c.timeScale().fitContent();
  }, [series]);

  const hasAny = useMemo(() => series.some((s) => s.points.length > 0), [series]);

  return (
    <div className="w-full rounded-lg border border-zinc-800 bg-zinc-900/30 p-2">
      <div className="h-[420px] w-full">
        <div ref={containerRef} className="h-full w-full" />
      </div>
      {!hasAny && (
        <div className="px-2 py-2 text-[11px] text-zinc-500">
          비교할 종목을 추가하면 차트가 그려집니다.
        </div>
      )}
    </div>
  );
}
