"use client";

import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import {
  CandlestickSeries,
  ColorType,
  HistogramSeries,
  createChart,
  type IChartApi,
  type ISeriesApi,
  type Time,
} from "lightweight-charts";
import { ChevronDown } from "lucide-react";
import { toast } from "sonner";
import { fetchStockChart } from "@/features/dashboard/apis/kis-stock.api";
import type { KisRuntimeCredentials } from "@/features/dashboard/store/use-kis-runtime-store";
import type {
  DashboardChartTimeframe,
  StockCandlePoint,
} from "@/features/dashboard/types/dashboard.types";
import { cn } from "@/lib/utils";
import {
  type ChartBar,
  convertCandleToBar,
  formatPrice,
  formatSignedPercent,
  isMinuteTimeframe,
  mergeBars,
  normalizeCandles,
  upsertRealtimeBar,
} from "./chart-utils";

const UP_COLOR = "#ef4444";
const DOWN_COLOR = "#2563eb";

// 분봉 드롭다운 옵션
const MINUTE_TIMEFRAMES: Array<{
  value: DashboardChartTimeframe;
  label: string;
}> = [
  { value: "1m", label: "1분" },
  { value: "30m", label: "30분" },
  { value: "1h", label: "1시간" },
];

// 일봉 이상 개별 버튼
const PERIOD_TIMEFRAMES: Array<{
  value: DashboardChartTimeframe;
  label: string;
}> = [
  { value: "1d", label: "일" },
  { value: "1w", label: "주" },
];

// ChartBar 타입은 chart-utils.ts에서 import

interface StockLineChartProps {
  symbol?: string;
  candles: StockCandlePoint[];
  credentials?: KisRuntimeCredentials | null;
}

/**
 * @description TradingView 스타일 캔들 차트 + 거래량 + 무한 과거 로딩
 * @see https://tradingview.github.io/lightweight-charts/tutorials/demos/realtime-updates
 * @see https://tradingview.github.io/lightweight-charts/tutorials/demos/infinite-history
 */
export function StockLineChart({
  symbol,
  candles,
  credentials,
}: StockLineChartProps) {
  const containerRef = useRef<HTMLDivElement | null>(null);
  const chartRef = useRef<IChartApi | null>(null);
  const candleSeriesRef = useRef<ISeriesApi<"Candlestick", Time> | null>(null);
  const volumeSeriesRef = useRef<ISeriesApi<"Histogram", Time> | null>(null);

  const [timeframe, setTimeframe] = useState<DashboardChartTimeframe>("1d");
  const [isMinuteDropdownOpen, setIsMinuteDropdownOpen] = useState(false);
  const [bars, setBars] = useState<ChartBar[]>([]);
  const [nextCursor, setNextCursor] = useState<string | null>(null);
  const [isLoading, setIsLoading] = useState(false);
  const [isLoadingMore, setIsLoadingMore] = useState(false);
  const [isChartReady, setIsChartReady] = useState(false);
  const lastRealtimeKeyRef = useRef<string>("");
  const loadingMoreRef = useRef(false);
  const loadMoreHandlerRef = useRef<() => Promise<void>>(async () => {});
  const initialLoadCompleteRef = useRef(false);

  // candles prop을 ref로 관리하여 useEffect 디펜던시에서 제거 (무한 페칭 방지)
  const latestCandlesRef = useRef(candles);
  useEffect(() => {
    latestCandlesRef.current = candles;
  }, [candles]);

  const latest = bars.at(-1);
  const prevClose = bars.length > 1 ? (bars.at(-2)?.close ?? 0) : 0;
  const change = latest ? latest.close - prevClose : 0;
  const changeRate = prevClose > 0 ? (change / prevClose) * 100 : 0;
  const renderableBars = useMemo(() => {
    const dedup = new Map<number, ChartBar>();
    for (const bar of bars) {
      if (
        !Number.isFinite(bar.time) ||
        !Number.isFinite(bar.open) ||
        !Number.isFinite(bar.high) ||
        !Number.isFinite(bar.low) ||
        !Number.isFinite(bar.close) ||
        bar.close <= 0
      ) {
        continue;
      }
      dedup.set(bar.time, bar);
    }
    return [...dedup.values()].sort((a, b) => a.time - b.time);
  }, [bars]);

  const setSeriesData = useCallback((nextBars: ChartBar[]) => {
    const candleSeries = candleSeriesRef.current;
    const volumeSeries = volumeSeriesRef.current;
    if (!candleSeries || !volumeSeries) return;

    // lightweight-charts는 시간 오름차순/유효 숫자 조건이 깨지면 렌더를 멈출 수 있어
    // 전달 직전 데이터를 한 번 더 정리합니다.
    const safeBars = nextBars;

    try {
      candleSeries.setData(
        safeBars.map((bar) => ({
          time: bar.time,
          open: bar.open,
          high: bar.high,
          low: bar.low,
          close: bar.close,
        })),
      );

      volumeSeries.setData(
        safeBars.map((bar) => ({
          time: bar.time,
          value: Number.isFinite(bar.volume) ? bar.volume : 0,
          color:
            bar.close >= bar.open
              ? "rgba(239,68,68,0.45)"
              : "rgba(37,99,235,0.45)",
        })),
      );
    } catch (error) {
      console.error("Failed to render chart series data:", error);
    }
  }, []);

  const handleLoadMore = useCallback(async () => {
    // 분봉은 당일 데이터만 제공되므로 과거 로딩 불가
    if (isMinuteTimeframe(timeframe)) return;
    if (!symbol || !credentials || !nextCursor || loadingMoreRef.current)
      return;

    loadingMoreRef.current = true;
    setIsLoadingMore(true);
    try {
      const response = await fetchStockChart(
        symbol,
        timeframe,
        credentials,
        nextCursor,
      );
      const older = normalizeCandles(response.candles, timeframe);
      setBars((prev) => mergeBars(older, prev));
      setNextCursor(response.hasMore ? response.nextCursor : null);
    } catch (error) {
      const msg =
        error instanceof Error
          ? error.message
          : "과거 차트 조회에 실패했습니다.";
      toast.error(msg);
    } finally {
      loadingMoreRef.current = false;
      setIsLoadingMore(false);
    }
  }, [credentials, nextCursor, symbol, timeframe]);

  useEffect(() => {
    loadMoreHandlerRef.current = handleLoadMore;
  }, [handleLoadMore]);

  useEffect(() => {
    const container = containerRef.current;
    if (!container || chartRef.current) return;

    const initialWidth = Math.max(container.clientWidth, 320);
    const initialHeight = Math.max(container.clientHeight, 340);

    const chart = createChart(container, {
      width: initialWidth,
      height: initialHeight,
      layout: {
        background: { type: ColorType.Solid, color: "#ffffff" },
        textColor: "#475569",
        attributionLogo: true,
      },
      localization: {
        locale: "ko-KR",
      },
      rightPriceScale: {
        borderColor: "#e2e8f0",
        scaleMargins: {
          top: 0.08,
          bottom: 0.24,
        },
      },
      grid: {
        vertLines: { color: "#edf1f5" },
        horzLines: { color: "#edf1f5" },
      },
      crosshair: {
        vertLine: { color: "#94a3b8", width: 1, style: 2 },
        horzLine: { color: "#94a3b8", width: 1, style: 2 },
      },
      timeScale: {
        borderColor: "#e2e8f0",
        timeVisible: true,
        secondsVisible: false,
        rightOffset: 2,
      },
      handleScroll: {
        mouseWheel: true,
        pressedMouseMove: true,
      },
      handleScale: {
        mouseWheel: true,
        pinch: true,
        axisPressedMouseMove: true,
      },
    });

    const candleSeries = chart.addSeries(CandlestickSeries, {
      upColor: UP_COLOR,
      downColor: DOWN_COLOR,
      wickUpColor: UP_COLOR,
      wickDownColor: DOWN_COLOR,
      borderUpColor: UP_COLOR,
      borderDownColor: DOWN_COLOR,
      priceLineVisible: true,
      lastValueVisible: true,
    });

    const volumeSeries = chart.addSeries(HistogramSeries, {
      priceScaleId: "volume",
      priceLineVisible: false,
      lastValueVisible: false,
      base: 0,
    });

    chart.priceScale("volume").applyOptions({
      scaleMargins: {
        top: 0.78,
        bottom: 0,
      },
      borderVisible: false,
    });

    // 스크롤 디바운스 타이머
    let scrollTimeout: NodeJS.Timeout | null = null;

    chart.timeScale().subscribeVisibleLogicalRangeChange((range) => {
      if (!range) return;

      // 분봉은 당일 데이터만 제공되므로 무한 스크롤 비활성화
      if (range.from < 10 && initialLoadCompleteRef.current) {
        if (scrollTimeout) clearTimeout(scrollTimeout);
        scrollTimeout = setTimeout(() => {
          void loadMoreHandlerRef.current();
        }, 300);
      }
    });

    chartRef.current = chart;
    candleSeriesRef.current = candleSeries;
    volumeSeriesRef.current = volumeSeries;
    setIsChartReady(true);

    const resizeObserver = new ResizeObserver(() => {
      const nextWidth = Math.max(container.clientWidth, 320);
      const nextHeight = Math.max(container.clientHeight, 340);
      chart.resize(nextWidth, nextHeight);
    });
    resizeObserver.observe(container);

    // 첫 렌더 직후 부모 레이아웃 계산이 끝난 시점에 한 번 더 사이즈를 맞춥니다.
    const rafId = window.requestAnimationFrame(() => {
      const nextWidth = Math.max(container.clientWidth, 320);
      const nextHeight = Math.max(container.clientHeight, 340);
      chart.resize(nextWidth, nextHeight);
    });

    return () => {
      window.cancelAnimationFrame(rafId);
      resizeObserver.disconnect();
      chart.remove();
      chartRef.current = null;
      candleSeriesRef.current = null;
      volumeSeriesRef.current = null;
      setIsChartReady(false);
    };
  }, []);

  useEffect(() => {
    if (symbol && credentials) return;
    setBars(normalizeCandles(candles, "1d"));
    setNextCursor(null);
  }, [candles, credentials, symbol]);

  useEffect(() => {
    if (!symbol || !credentials) return;

    // 초기 로딩 보호 플래그 초기화 (타임프레임/종목 변경 시)
    initialLoadCompleteRef.current = false;

    let disposed = false;
    const load = async () => {
      setIsLoading(true);
      try {
        const response = await fetchStockChart(symbol, timeframe, credentials);
        if (disposed) return;

        const normalized = normalizeCandles(response.candles, timeframe);
        setBars(normalized);
        setNextCursor(response.hasMore ? response.nextCursor : null);

        // 초기 로딩 완료 후 500ms 지연 후 무한 스크롤 활성화
        // (fitContent 후 range가 0이 되어 즉시 트리거되는 것 방지)
        setTimeout(() => {
          if (!disposed) {
            initialLoadCompleteRef.current = true;
          }
        }, 500);
      } catch (error) {
        if (disposed) return;
        const message =
          error instanceof Error ? error.message : "차트 조회에 실패했습니다.";
        toast.error(message);
        // 에러 발생 시 fallback으로 props로 전달된 candles 사용
        setBars(normalizeCandles(latestCandlesRef.current, timeframe));
        setNextCursor(null);
      } finally {
        if (!disposed) setIsLoading(false);
      }
    };

    void load();
    return () => {
      disposed = true;
    };
  }, [credentials, symbol, timeframe]);

  useEffect(() => {
    if (!isChartReady) return;
    setSeriesData(renderableBars);

    // 초기 로딩 시에만 fitContent 수행
    if (!initialLoadCompleteRef.current && renderableBars.length > 0) {
      chartRef.current?.timeScale().fitContent();
    }
  }, [isChartReady, renderableBars, setSeriesData]);

  const latestRealtime = useMemo(() => candles.at(-1), [candles]);
  useEffect(() => {
    if (!latestRealtime || bars.length === 0) return;
    if (timeframe === "1w" && !latestRealtime.timestamp && !latestRealtime.time)
      return;

    const key = `${latestRealtime.time}-${latestRealtime.price}-${latestRealtime.volume ?? 0}`;
    if (lastRealtimeKeyRef.current === key) return;
    lastRealtimeKeyRef.current = key;

    const nextBar = convertCandleToBar(latestRealtime, timeframe);
    if (!nextBar) return;

    setBars((prev) => upsertRealtimeBar(prev, nextBar));
  }, [bars.length, candles, latestRealtime, timeframe]);

  // 상태 메시지 결정 (오버레이로 표시)
  const statusMessage = (() => {
    if (isLoading && bars.length === 0)
      return "차트 데이터를 불러오는 중입니다.";
    if (bars.length === 0) return "차트 데이터가 없습니다.";
    if (renderableBars.length === 0)
      return "차트 데이터 형식이 올바르지 않아 렌더링할 수 없습니다.";
    return null;
  })();

  return (
    <div className="flex h-full min-h-[340px] flex-col bg-white">
      {/* ========== CHART TOOLBAR ========== */}
      <div className="flex flex-wrap items-center justify-between gap-2 border-b border-slate-200 px-2 py-2 sm:px-3">
        <div className="flex flex-wrap items-center gap-1 text-xs sm:text-sm">
          {/* 분봉 드롭다운 */}
          <div className="relative">
            <button
              type="button"
              onClick={() => setIsMinuteDropdownOpen((v) => !v)}
              onBlur={() =>
                setTimeout(() => setIsMinuteDropdownOpen(false), 200)
              }
              className={cn(
                "flex items-center gap-1 rounded px-2 py-1 text-slate-600 transition-colors hover:bg-slate-100",
                MINUTE_TIMEFRAMES.some((t) => t.value === timeframe) &&
                  "bg-brand-100 font-semibold text-brand-700",
              )}
            >
              {MINUTE_TIMEFRAMES.find((t) => t.value === timeframe)?.label ??
                "분봉"}
              <ChevronDown className="h-3 w-3" />
            </button>
            {isMinuteDropdownOpen && (
              <div className="absolute left-0 top-full z-10 mt-1 rounded border border-slate-200 bg-white shadow-lg">
                {MINUTE_TIMEFRAMES.map((item) => (
                  <button
                    key={item.value}
                    type="button"
                    onClick={() => {
                      setTimeframe(item.value);
                      setIsMinuteDropdownOpen(false);
                    }}
                    className={cn(
                      "block w-full whitespace-nowrap px-3 py-1.5 text-left hover:bg-slate-100",
                      timeframe === item.value &&
                        "bg-brand-50 font-semibold text-brand-700",
                    )}
                  >
                    {item.label}
                  </button>
                ))}
              </div>
            )}
          </div>

          {/* 일/주 버튼 */}
          {PERIOD_TIMEFRAMES.map((item) => (
            <button
              key={item.value}
              type="button"
              onClick={() => setTimeframe(item.value)}
              className={cn(
                "rounded px-2 py-1 text-slate-600 transition-colors hover:bg-slate-100",
                timeframe === item.value &&
                  "bg-brand-100 font-semibold text-brand-700",
              )}
            >
              {item.label}
            </button>
          ))}
          {isLoadingMore && (
            <span className="ml-2 text-[11px] text-muted-foreground">
              과거 데이터 로딩중...
            </span>
          )}
        </div>
        <div className="text-[11px] text-slate-600 sm:text-xs">
          O {formatPrice(latest?.open ?? 0)} H {formatPrice(latest?.high ?? 0)}{" "}
          L {formatPrice(latest?.low ?? 0)} C{" "}
          <span className={cn(change >= 0 ? "text-red-600" : "text-blue-600")}>
            {formatPrice(latest?.close ?? 0)} ({formatSignedPercent(changeRate)}
            )
          </span>
        </div>
      </div>

      {/* ========== CHART BODY ========== */}
      <div className="relative min-h-0 flex-1 overflow-hidden">
        {/* 차트 캔버스 컨테이너 - 항상 렌더링 */}
        <div ref={containerRef} className="h-full w-full" />

        {/* 상태 메시지 오버레이 */}
        {statusMessage && (
          <div className="absolute inset-0 flex items-center justify-center bg-white/80 text-sm text-muted-foreground">
            {statusMessage}
          </div>
        )}
      </div>
    </div>
  );
}
