import { useState, useEffect, useRef, useCallback } from 'react';
import type { OHLCVBar, Timeframe } from '../types';
import {
  UpbitWebSocketClient,
  getCandleStart,
  type UpbitTrade,
  type UpbitMessage,
} from '../utils/upbitApi';
import { OBV_DEEP_HISTORY } from '../utils/upbitApi';
import { fetchUpbitCandlesCached, invalidateMarketCache } from '../utils/requestCache';

export type WsStatus = 'connecting' | 'connected' | 'disconnected' | 'error';

interface UpbitDataOptions {
  /**
   * 같은 캔들 내 틱 수신 시 콜백.
   * React state 변경 없이 차트를 직접 업데이트한다.
   */
  onTickUpdate: (bar: OHLCVBar) => void;
  /**
   * 새 캔들이 시작될 때 콜백 (완성된 신규 bar 전달).
   * React state 변경 없이 차트에 바를 추가한다.
   */
  onNewCandle:  (newBar: OHLCVBar) => void;
  /** false면 REST/WebSocket 모두 비활성화 (시뮬레이션 모드용) */
  enabled?:     boolean;
  /** OBV 등 누적 지표: 상장 이력에 가깝게 장기 캔들 로드 */
  deepObvHistory?: boolean;
}

interface UseUpbitDataReturn {
  /** 초기 200개 캔들 (심볼/타임프레임 변경 시에만 갱신) */
  bars:       OHLCVBar[];
  /** bars 가 어느 종목 기준인지 (로딩 중·전환 직후 null) */
  barsMarket: string | null;
  /** 현재 진행 중인 캔들의 최신 스냅샷 (UI 표시용) */
  latestBar:  OHLCVBar | null;
  wsStatus:   WsStatus;
  isLoading:  boolean;
  error:      string | null;
}

/** 화면에 표시할 캔들 수 */
export const DISPLAY_COUNT = 200;
/**
 * 지표 워밍업 기간 (안전 마진 포함)
 * - 일목균형표 Span B: 52바
 * - OBV 누적: 과거 봉이 많을수록 업비트 절대값에 근접 → 600바 워밍업
 */
export const WARMUP_COUNT  = 600;
/** OBV 사용 시 초기 페치 봉 수 */
export { OBV_DEEP_HISTORY };

export function useUpbitData(
  market:    string,
  timeframe: Timeframe,
  opts:      UpbitDataOptions,
): UseUpbitDataReturn {
  // bars = 초기 FETCH_COUNT개 (REST), 실시간 수신 시 변경 안 함 → TradingChart setData 재호출 방지
  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;

  // ── 1. REST API: 초기 캔들 로딩 (표시용 + 워밍업 합산) ─────────────────
  // 이전 market 값을 기억해 종목 변경 시에만 캐시를 무효화한다.
  const prevMarketRef = useRef<string>('');

  useEffect(() => {
    if (opts.enabled === false) return;

    let cancelled = false;
    setIsLoading(true);
    setError(null);
    setBars([]);
    setBarsMarket(null);
    setLatestBar(null);
    barsRef.current = [];

    // 종목(market)이 변경된 경우에는 캐시를 무효화해 신규 요청을 강제한다.
    // 타임프레임만 바뀐 경우도 캐시 키가 다르므로 자동으로 새 요청이 발생한다.
    const marketChanged = prevMarketRef.current !== '' && prevMarketRef.current !== market;
    prevMarketRef.current = market;

    const fetchCount = opts.deepObvHistory
      ? OBV_DEEP_HISTORY
      : DISPLAY_COUNT + WARMUP_COUNT;

    fetchUpbitCandlesCached(market, timeframe, fetchCount, marketChanged)
      .then(newBars => {
        if (cancelled) return;
        barsRef.current = newBars;
        setBarsMarket(market);
        setBars(newBars);
        setLatestBar(newBars[newBars.length - 1] ?? null);
        setIsLoading(false);
      })
      .catch(err => {
        if (cancelled) return;
        setError(`데이터 로딩 실패: ${(err as Error).message}`);
        setIsLoading(false);
      });

    return () => { cancelled = true; };
  }, [market, timeframe, opts.enabled, opts.deepObvHistory]);

  // ── 2. WebSocket: 실시간 체결 구독 ────────────────────────────────────
  const handleMessage = useCallback((msg: UpbitMessage) => {
    if (msg.type !== 'trade') return;
    const trade = msg as UpbitTrade;

    const currentBars = barsRef.current;
    if (currentBars.length === 0) return;

    const candleStart = getCandleStart(trade.trade_timestamp, timeframe);
    const lastBar     = currentBars[currentBars.length - 1];

    if (lastBar.time === candleStart) {
      // ── 동일 캔들: close/high/low/volume 갱신 ──────────────────────────
      const updated: OHLCVBar = {
        ...lastBar,
        close:  trade.trade_price,
        high:   Math.max(lastBar.high, trade.trade_price),
        low:    Math.min(lastBar.low,  trade.trade_price),
        volume: lastBar.volume + trade.trade_volume,
      };
      barsRef.current[currentBars.length - 1] = updated;

      // UI 표시용 latestBar만 갱신 (bars state는 변경하지 않음)
      setLatestBar(updated);

      // 차트 직접 업데이트 (React re-render 없음)
      optsRef.current.onTickUpdate(updated);

    } else if (candleStart > lastBar.time) {
      // ── 새 캔들: barsRef에만 추가, bars state는 건드리지 않음 ──────────
      const newBar: OHLCVBar = {
        time:   candleStart,
        open:   trade.trade_price,
        high:   trade.trade_price,
        low:    trade.trade_price,
        close:  trade.trade_price,
        volume: trade.trade_volume,
      };
      barsRef.current = [...barsRef.current, newBar];

      setLatestBar(newBar);

      // 차트에 새 바만 추가 + 인디케이터 last point 업데이트 (React re-render 없음)
      optsRef.current.onNewCandle(newBar);
    }
  }, [timeframe]);

  useEffect(() => {
    if (opts.enabled === false) return;

    setWsStatus('connecting');
    setError(null);   // 재연결 시 기존 오류 초기화

    const client = new UpbitWebSocketClient({
      market,
      onMessage:    handleMessage,
      onConnect:    () => { setWsStatus('connected'); setError(null); },
      onDisconnect: () => setWsStatus('disconnected'),
      // 연결 끊김은 자동 재연결되므로 에러 표시만 (상태는 disconnected 유지)
      onError:      (e) => { console.warn('[UpbitWS]', e); },
    });
    return () => client.destroy();
  }, [market, timeframe, handleMessage, opts.enabled]);

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