/**
 * 백엔드 STOMP /sub/charts/{market}/{candleType} 실시간 캔들 (Blueprint 중앙 집중 경로)
 */
import { useState, useEffect, useRef, useCallback } from 'react';
import type { OHLCVBar, Timeframe } from '../types';
import { parseStompJson } from '../utils/stompMessage';
import { subscribeStompTopic } from '../utils/stompChartBroker';
import { timeframeToCandleType } from '../utils/chartCandleType';
import { getCandleStart, TF_SECONDS } from '../utils/upbitApi';
import { DISPLAY_COUNT, WARMUP_COUNT } from './useUpbitData';
import type { WsStatus } from './useUpbitData';
import { API_BASE } from '../utils/backendApi';

/** 백엔드 BarBuilder 는 틱마다 1m 만 STOMP 발행 — 차트 분봉은 클라이언트에서 집계 */
const STOMP_TICK_CANDLE_TYPE = '1m';

/** 표시 분봉 버킷 — 히스토리 마지막 봉과 STOMP time 불일치 시 마지막 봉 갱신용 */
function resolveChartBarTime(
  bucketTime: number,
  displayTf: Timeframe,
  lastChartTime: number | undefined,
): number {
  if (lastChartTime == null || bucketTime >= lastChartTime) return bucketTime;
  const tfSec = TF_SECONDS[displayTf] ?? 60;
  if (lastChartTime - bucketTime < tfSec) return lastChartTime;
  return bucketTime;
}

async function pinChartWatch(market: string, candleType: string): Promise<void> {
  try {
    await fetch(`${API_BASE}/candles/watch?market=${encodeURIComponent(market)}&type=${encodeURIComponent(candleType)}`, {
      method: 'POST',
    });
  } catch {
    /* watch 실패해도 STOMP 구독은 계속 */
  }
}

interface StompCandlePayload {
  time: number;
  open: number;
  high: number;
  low: number;
  close: number;
  volume: number;
}

interface Options {
  onTickUpdate: (bar: OHLCVBar) => void;
  onNewCandle: (bar: OHLCVBar) => void;
  enabled?: boolean;
}

interface Return {
  bars: OHLCVBar[];
  /** bars 가 어느 종목 기준인지 (로딩 중·전환 직후 null) */
  barsMarket: string | null;
  latestBar: OHLCVBar | null;
  wsStatus: WsStatus;
  isLoading: boolean;
  error: string | null;
}

async function fetchInitialBars(market: string, candleType: string, count: number): Promise<OHLCVBar[]> {
  const params = new URLSearchParams({
    market,
    type: candleType,
    count: String(count),
  });
  const res = await fetch(`${API_BASE}/candles/history?${params}`);
  if (!res.ok) throw new Error(`history ${res.status}`);
  const data = (await res.json()) as StompCandlePayload[];
  return data.map(b => ({
    time: b.time,
    open: b.open,
    high: b.high,
    low: b.low,
    close: b.close,
    volume: b.volume,
  }));
}

async function fetchInitialBarsWithRetry(
  market: string,
  candleType: string,
  count: number,
  attempts = 3,
): Promise<OHLCVBar[]> {
  let lastErr: Error | null = null;
  for (let i = 0; i < attempts; i++) {
    try {
      return await fetchInitialBars(market, candleType, count);
    } catch (e) {
      lastErr = e as Error;
      if (i < attempts - 1) {
        await new Promise(r => setTimeout(r, 400 * (i + 1)));
      }
    }
  }
  throw lastErr ?? new Error('history fetch failed');
}

export function useStompChartData(
  market: string,
  timeframe: Timeframe,
  opts: Options,
): Return {
  const candleType = timeframeToCandleType(timeframe);
  const [bars, setBars] = useState<OHLCVBar[]>([]);
  const [barsMarket, setBarsMarket] = useState<string | null>(null);
  const [latestBar, setLatestBar] = useState<OHLCVBar | null>(null);
  const [wsStatus, setWsStatus] = useState<WsStatus>('connecting');
  const [isLoading, setIsLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);

  const barsRef = useRef<OHLCVBar[]>([]);
  const optsRef = useRef(opts);
  optsRef.current = opts;
  const lastTimeRef = useRef<number>(0);
  /** 1m 봉 volume 스냅샷 — 상위 분봉 volume 델타 계산용 */
  const last1mVolRef = useRef<number>(0);
  const last1mTimeRef = useRef<number>(0);
  /** 초기 히스토리 로드 완료 전에는 차트 직접 갱신 콜백을 호출하지 않음 */
  const historyReadyRef = useRef(false);

  useEffect(() => {
    if (opts.enabled === false) return;
    let cancelled = false;
    historyReadyRef.current = false;
    setIsLoading(true);
    setError(null);
    setBars([]);
    setBarsMarket(null);
    barsRef.current = [];
    last1mVolRef.current = 0;
    last1mTimeRef.current = 0;
    lastTimeRef.current = 0;

    const count = DISPLAY_COUNT + WARMUP_COUNT;
    fetchInitialBarsWithRetry(market, candleType, count)
      .then(newBars => {
        if (cancelled) return;
        barsRef.current = newBars;
        historyReadyRef.current = true;
        setBarsMarket(market);
        setBars(newBars);
        setLatestBar(newBars[newBars.length - 1] ?? null);
        lastTimeRef.current = newBars[newBars.length - 1]?.time ?? 0;
        setIsLoading(false);
      })
      .catch(err => {
        if (cancelled) return;
        setError(`STOMP 초기 로딩 실패: ${(err as Error).message}`);
        setIsLoading(false);
      });
    return () => { cancelled = true; };
  }, [market, candleType, opts.enabled]);

  const applyCandle = useCallback((data: StompCandlePayload, displayTf: Timeframe) => {
    if (!historyReadyRef.current) return;
    const oneMin: OHLCVBar = {
      time: data.time,
      open: data.open,
      high: data.high,
      low: data.low,
      close: data.close,
      volume: data.volume,
    };

    const pushBar = (bar: OHLCVBar) => {
      const current = barsRef.current;
      const notifyChart = historyReadyRef.current;
      if (current.length === 0) {
        barsRef.current = [bar];
        setLatestBar(bar);
        if (notifyChart) optsRef.current.onNewCandle(bar);
        lastTimeRef.current = bar.time;
        return;
      }
      const last = current[current.length - 1];
      if (bar.time === last.time) {
        barsRef.current[current.length - 1] = bar;
        setLatestBar(bar);
        if (notifyChart) optsRef.current.onTickUpdate(bar);
      } else if (bar.time > last.time) {
        barsRef.current = [...current, bar];
        setLatestBar(bar);
        if (notifyChart) optsRef.current.onNewCandle(bar);
      } else {
        const patched = { ...bar, time: last.time };
        barsRef.current[current.length - 1] = patched;
        setLatestBar(patched);
        if (notifyChart) optsRef.current.onTickUpdate(patched);
      }
      lastTimeRef.current = bar.time;
    };

    const lastChart = barsRef.current[barsRef.current.length - 1];

    // 1m 차트: STOMP 페이로드를 그대로 사용
    if (displayTf === '1m') {
      const t = resolveChartBarTime(oneMin.time, '1m', lastChart?.time);
      pushBar({ ...oneMin, time: t });
      return;
    }

    // 상위 분봉: 1m 틱을 useUpbitData 와 동일하게 집계
    const candleStart = resolveChartBarTime(
      getCandleStart(oneMin.time * 1000, displayTf),
      displayTf,
      lastChart?.time,
    );
    const current = barsRef.current;
    if (current.length === 0) {
      pushBar({
        time: candleStart,
        open: oneMin.open,
        high: oneMin.high,
        low: oneMin.low,
        close: oneMin.close,
        volume: oneMin.volume,
      });
      last1mTimeRef.current = oneMin.time;
      last1mVolRef.current = oneMin.volume;
      return;
    }

    let volDelta = oneMin.volume;
    if (oneMin.time === last1mTimeRef.current) {
      volDelta = oneMin.volume - last1mVolRef.current;
    } else {
      last1mTimeRef.current = oneMin.time;
    }
    last1mVolRef.current = oneMin.volume;

    const last = current[current.length - 1];
    if (last.time === candleStart) {
      const updated: OHLCVBar = {
        time: candleStart,
        open: last.open,
        high: Math.max(last.high, oneMin.high),
        low: Math.min(last.low, oneMin.low),
        close: oneMin.close,
        volume: Math.max(0, last.volume + volDelta),
      };
      pushBar(updated);
    } else if (candleStart > last.time) {
      pushBar({
        time: candleStart,
        open: oneMin.open,
        high: oneMin.high,
        low: oneMin.low,
        close: oneMin.close,
        volume: oneMin.volume,
      });
      last1mTimeRef.current = oneMin.time;
      last1mVolRef.current = oneMin.volume;
    }
  }, []);

  useEffect(() => {
    if (opts.enabled === false) {
      setWsStatus('disconnected');
      return;
    }

    setWsStatus('connecting');
    setError(null);
    last1mVolRef.current = 0;
    last1mTimeRef.current = 0;
    void pinChartWatch(market, STOMP_TICK_CANDLE_TYPE);
    if (candleType !== STOMP_TICK_CANDLE_TYPE) {
      void pinChartWatch(market, candleType);
    }

    const topic = `/sub/charts/${market}/${STOMP_TICK_CANDLE_TYPE}`;
    let alive = true;
    const unsub = subscribeStompTopic(topic, msg => {
      if (!alive) return;
      setWsStatus('connected');
      const data = parseStompJson<StompCandlePayload>(msg);
          if (data?.time == null || typeof data.time !== 'number') return;
      applyCandle(data, timeframe);
    });

    const t = window.setTimeout(() => setWsStatus('connected'), 800);

    return () => {
      alive = false;
      window.clearTimeout(t);
      unsub();
      setWsStatus('disconnected');
    };
  }, [market, timeframe, opts.enabled, applyCandle]);

  return { bars, barsMarket, latestBar, wsStatus, isLoading, error };
}
