/**
 * Market Data WebSocket Hook
 * - Backend RealtimeWebSocketHandler와 연결 (기존 Python Broadcast Server 대체)
 * - Native WebSocket 사용
 * - 자동 재연결
 */
import { useState, useEffect, useRef, useCallback } from 'react';
import { getApiBaseUrl } from '../services/apiConfig';
import { timeframeToIntervalMs } from '../utils/investmentChart/wsCandleHelpers';

interface Candle {
  timestamp: number;  // 기존 코드와 호환성 유지
  time?: number | string;  // ✅ CandlestickTestPage onData에서 사용
  open: number;
  high: number;
  low: number;
  close: number;
  volume: number;
  is_closed?: boolean;
  trade_count?: number;
}

interface Indicators {
  // RSI
  rsi_14?: number;
  
  // EMA
  ema_9?: number;
  ema_21?: number;
  
  // MACD
  macd?: number;
  macd_signal?: number;
  macd_histogram?: number;
  
  // Bollinger Bands
  bb_upper?: number;
  bb_middle?: number;
  bb_lower?: number;
  
  // Stochastic
  stoch_k?: number;
  stoch_d?: number;
  
  // ADX
  adx?: number;
  plus_di?: number;
  minus_di?: number;
  /** OBV 시그널 (RealtimeWebSocketHandler snake_case) */
  obv_signal?: number;
  
  // Moving Averages (SMA) - 백엔드 계산
  ma_5?: number;
  ma_10?: number;
  ma_20?: number;
  ma_60?: number;
  ma_120?: number;
  
  // Ichimoku Cloud (일목균형표) - 백엔드 계산
  ichimoku_tenkan?: number;
  ichimoku_kijun?: number;
  ichimoku_senkou_a?: number;
  ichimoku_senkou_b?: number;
  ichimoku_chikou?: number;
}

interface UseMarketDataWebSocketOptions {
  market: string;
  timeframe: string;
  enabled?: boolean;
  onData?: (data: { candles: Candle[]; indicators: Indicators; isUpdate?: boolean }) => void;
  onError?: (error: any) => void;
}

type ConnectionStatus = 'disconnected' | 'connecting' | 'connected' | 'error';

/** Native 실시간 캔들 — API 베이스 하위(/api/ws/realtime)로만 연결 (nginx가 /api/* 만 프록시하는 경우 대응) */
const getRealtimeWebSocketEndpoint = (): string => {
  const envWsUrl = process.env.REACT_APP_WS_URL;
  if (envWsUrl) {
    const base = envWsUrl.replace(/\/+$/, '');
    if (/\/ws\//i.test(base) || /\/api\/ws\//i.test(base)) return base;
    return `${base}/api/ws/realtime`;
  }
  const apiBase = getApiBaseUrl().replace(/\/+$/, '');
  const wsBase = apiBase.replace(/^http:\/\//i, 'ws://').replace(/^https:\/\//i, 'wss://');
  return `${wsBase}/ws/realtime`;
};

const REALTIME_WS_ENDPOINT = getRealtimeWebSocketEndpoint();

function toEpochMs(ts: number | undefined): number {
  if (ts == null || !Number.isFinite(ts)) return NaN;
  return ts > 1e12 ? ts : ts * 1000;
}

export const useMarketDataWebSocket = ({
  market,
  timeframe,
  enabled = true,
  onData,
  onError,
}: UseMarketDataWebSocketOptions) => {
  const [status, setStatus] = useState<ConnectionStatus>('disconnected');
  const [candles, setCandles] = useState<Candle[]>([]);
  const [indicators, setIndicators] = useState<Indicators>({});
  
  const wsRef = useRef<WebSocket | null>(null);
  const reconnectTimeoutRef = useRef<NodeJS.Timeout | null>(null);
  const reconnectAttemptsRef = useRef(0);
  const onDataRef = useRef(onData);
  const onErrorRef = useRef(onError);

  // Ref 업데이트
  useEffect(() => {
    onDataRef.current = onData;
    onErrorRef.current = onError;
  }, [onData, onError]);

  /**
   * 초기 스냅샷 로드 (REST API)
   */
  const loadInitialData = useCallback(async () => {
    const fetchWithRetry = async (attempt = 0): Promise<void> => {
      const maxRetries = 3;
      const retryDelay = 1500;
      try {
        const apiBase = getApiBaseUrl().replace(/\/+$/, '');
        const response = await fetch(
          `${apiBase}/realtime/candles?market=${encodeURIComponent(market)}&timeframe=${encodeURIComponent(timeframe)}&limit=200`
        );
        if (!response.ok) throw new Error(`HTTP ${response.status}`);
        const result = await response.json();
        
        if (result.success && result.data && result.data.length > 0) {
          // Backend 형식: { timestamp, open, high, low, close, volume, indicators }
          const candlesData = result.data.map((item: any) => ({
            timestamp: item.timestamp,  // 기존 코드와 호환
            open: item.open,
            high: item.high,
            low: item.low,
            close: item.close,
            volume: item.volume,
          }));
          
          setCandles(candlesData);
          
          // 마지막 캔들의 지표
          const lastIndicators = result.data[result.data.length - 1].indicators || {};
          setIndicators(lastIndicators);
          
          onDataRef.current?.({ candles: candlesData, indicators: lastIndicators });
          
          console.log('[Market Data] Initial data loaded:', candlesData.length, 'candles');
          return;
        }
        if (attempt < maxRetries) {
          await new Promise((r) => setTimeout(r, retryDelay));
          return fetchWithRetry(attempt + 1);
        }
        console.warn('[Market Data] No initial data available after retries');
      } catch (error) {
        if (attempt < maxRetries) {
          await new Promise((r) => setTimeout(r, retryDelay));
          return fetchWithRetry(attempt + 1);
        }
        console.error('[Market Data] Failed to load initial data:', error);
        onErrorRef.current?.(error);
      }
    };
    await fetchWithRetry();
  }, [market, timeframe]);

  /**
   * WebSocket 연결
   */
  const connect = useCallback(() => {
    console.log(`[Market Data] WebSocket connect called - enabled: ${enabled}, market: ${market}, timeframe: ${timeframe}`);
    if (!enabled) {
      console.log('[Market Data] WebSocket disabled, skipping connection');
      return;
    }
    
    // 이미 연결 중이거나 연결되어 있으면 스킵
    if (wsRef.current) {
      const state = wsRef.current.readyState;
      if (state === WebSocket.CONNECTING || state === WebSocket.OPEN) {
        if (state === WebSocket.OPEN) {
          setStatus('connected');
        }
        console.log(`[Market Data] Already connecting/connected (readyState: ${state})`);
        return;
      }
    }
    
    setStatus('connecting');
    console.log(`[Market Data] Connecting to ${REALTIME_WS_ENDPOINT}...`);
    console.log(`[Market Data] Market: ${market}, Timeframe: ${timeframe}`);
    
    const ws = new WebSocket(REALTIME_WS_ENDPOINT);
    
    ws.onopen = () => {
      console.log('✅ [Market Data] WebSocket connected successfully!');
      setStatus('connected');
      reconnectAttemptsRef.current = 0;
      
      // 구독 요청
      const subscribeMsg = {
        type: 'subscribe',
        market,
        timeframe,
      };
      ws.send(JSON.stringify(subscribeMsg));
      
      console.log(`📡 [Market Data] Subscription request sent:`, subscribeMsg);
      
      // 초기 데이터 로드
      loadInitialData();
    };
    
    ws.onmessage = (event) => {
      try {
        const data = JSON.parse(event.data);
        
        if (data.type === 'connected') {
          console.log(`[Market Data] Connection confirmed: ${data.sessionId}`);
          // 세션 확인 메시지 수신 = 소켓은 이미 OPEN — React 상태만 어긋난 경우 복구
          if (ws.readyState === WebSocket.OPEN) {
            setStatus('connected');
          }
        }
        else if (data.type === 'subscribed') {
          console.log(`[Market Data] Subscription confirmed: ${data.market} ${data.timeframe}`);
          if (ws.readyState === WebSocket.OPEN) {
            setStatus('connected');
          }
        }
        else if (data.type === 'update') {
          if (ws.readyState === WebSocket.OPEN) {
            setStatus('connected');
          }
          // 실시간 업데이트 - Backend 형식: candle.time
          const backendCandle = data.candle;
          
          // Backend의 time을 timestamp로 변환 (기존 코드 호환)
          const newCandle: Candle = {
            timestamp: backendCandle.time,
            open: backendCandle.open,
            high: backendCandle.high,
            low: backendCandle.low,
            close: backendCandle.close,
            volume: backendCandle.volume,
            is_closed: backendCandle.is_closed,
            trade_count: backendCandle.trade_count,
          };
          
          const newIndicators = data.indicators || {};
          
          console.log(`📊 [Market Data] WebSocket update 수신:`, {
            timestamp: newCandle.timestamp,
            close: newCandle.close,
            volume: newCandle.volume,
            is_closed: newCandle.is_closed,
          });
          
          setCandles((prevCandles) => {
            const updated = [...prevCandles];
            
            if (updated.length === 0) {
              // 첫 캔들
              const result = [newCandle];
              const payload = {
                candles: result,
                indicators: newIndicators,
                isUpdate: true as const,
              };
              // setState 업데이터 안에서 부모 setState/차트 갱신을 직접 호출하면 예외·배치 순서로 candles 커밋이 깨질 수 있음
              queueMicrotask(() => {
                try {
                  onDataRef.current?.(payload);
                } catch (cbErr) {
                  console.error('[Market Data] onData 콜백 오류 (첫 캔들):', cbErr);
                  onErrorRef.current?.(cbErr);
                }
              });
              return result;
            }
            
            const lastCandle = updated[updated.length - 1];
            const intervalMs = timeframeToIntervalMs(timeframe);
            const lastMs = toEpochMs(lastCandle.timestamp);
            const newMs = toEpochMs(newCandle.timestamp);
            const lastBucket = Number.isFinite(lastMs) ? Math.floor(lastMs / intervalMs) : NaN;
            const newBucket = Number.isFinite(newMs) ? Math.floor(newMs / intervalMs) : NaN;
            
            console.log(`🔍 [Market Data] 봉 버킷 비교:`, {
              timeframe,
              intervalMs,
              lastCandleTimestamp: lastCandle.timestamp,
              newCandleTimestamp: newCandle.timestamp,
              lastBucket,
              newBucket,
            });
            
            // ✅ 동일 타임스탬프가 아니어도, 같은 봉 구간이면 마지막 봉 갱신 (REST vs CandleEngine ms 불일치)
            if (Number.isFinite(lastBucket) && Number.isFinite(newBucket)) {
              if (newBucket < lastBucket) {
                console.warn(`⚠️ [Market Data] 과거 봉 무시: newBucket=${newBucket}, lastBucket=${lastBucket}`);
                return updated;
              }
              if (newBucket === lastBucket) {
                updated[updated.length - 1] = newCandle;
                console.log(`✅ [Market Data] 동일 봉 버킷 업데이트: bucket=${newBucket}`);
              } else {
                updated.push(newCandle);
                console.log(`✅ [Market Data] 새 봉 추가: bucket ${lastBucket} → ${newBucket}`);
                if (updated.length > 200) {
                  updated.shift();
                }
              }
            } else {
              // 폴백: ms 변환 실패 시 기존 엄격 비교
              if (newCandle.timestamp < lastCandle.timestamp) {
                console.warn(`⚠️ [Market Data] 과거 캔들 무시: new=${newCandle.timestamp}, last=${lastCandle.timestamp}`);
                return updated;
              }
              if (lastCandle.timestamp === newCandle.timestamp) {
                updated[updated.length - 1] = newCandle;
                console.log(`✅ [Market Data] 현재 캔들 업데이트(폴백): ${newCandle.timestamp}`);
              } else if (newCandle.timestamp > lastCandle.timestamp) {
                updated.push(newCandle);
                console.log(`✅ [Market Data] 새 캔들 추가(폴백): ${newCandle.timestamp}`);
                if (updated.length > 200) {
                  updated.shift();
                }
              }
            }
            
            const payload = {
              candles: updated,
              indicators: newIndicators,
              isUpdate: true as const,
            };
            queueMicrotask(() => {
              try {
                onDataRef.current?.(payload);
              } catch (cbErr) {
                console.error('[Market Data] onData 콜백 오류:', cbErr);
                onErrorRef.current?.(cbErr);
              }
            });

            return updated;
          });
          
          setIndicators(newIndicators);
        }
        else if (data.type === 'pong') {
          // Ping/Pong
        }
        else if (data.type === 'error') {
          console.error('[Market Data] Server error:', data.message);
          onErrorRef.current?.(new Error(data.message));
        }
        
      } catch (error) {
        console.error('[Market Data] Message parsing error:', error);
        onErrorRef.current?.(error);
      }
    };
    
    ws.onerror = (event) => {
      console.error('❌ [Market Data] WebSocket error:', event);
      console.error('[Market Data] Connection details:', {
        url: REALTIME_WS_ENDPOINT,
        market,
        timeframe,
        readyState: ws.readyState,
      });
      setStatus('error');
      onErrorRef.current?.(event);
    };
    
    ws.onclose = (event) => {
      console.log(`🔌 [Market Data] WebSocket closed: Code ${event.code}, Reason: "${event.reason || 'No reason'}"`);
      console.log(`[Market Data] Was clean close: ${event.wasClean}`);
      
      // wsRef.current가 이미 null이면 disconnect()가 호출된 것이므로 재연결 안함
      if (wsRef.current === ws) {
        wsRef.current = null;
      } else {
        console.log('[Market Data] WebSocket already cleaned up by disconnect(), skipping reconnect');
        return;
      }
      
      setStatus('disconnected');
      
      // 자동 재연결 (enabled일 때만, 지수 백오프·상한 완화 — 일시적 Nginx/백엔드 재시작 대응)
      const MAX_RETRIES = 25;
      
      if (enabled && !reconnectTimeoutRef.current && reconnectAttemptsRef.current < MAX_RETRIES) {
        const delay = Math.min(1000 * Math.pow(2, Math.min(reconnectAttemptsRef.current, 6)), 30000);
        reconnectAttemptsRef.current += 1;
        setStatus('connecting');

        console.log(`🔄 [Market Data] Auto-reconnect attempt #${reconnectAttemptsRef.current}/${MAX_RETRIES} in ${delay}ms...`);

        reconnectTimeoutRef.current = setTimeout(() => {
          reconnectTimeoutRef.current = null;
          connect();
        }, delay);
      } else if (reconnectAttemptsRef.current >= MAX_RETRIES) {
        console.error(`❌ [Market Data] ${MAX_RETRIES}회 재연결 실패`);
        console.log('💡 [Market Data] 연결 상태를 확인하세요:');
        console.log('   - Backend 서버: localhost:8080 실행 확인');
        console.log('   - WebSocket 엔드포인트: /api/ws/realtime (또는 /ws/realtime)');
      }
    };
    
    wsRef.current = ws;
    
  }, [enabled, market, timeframe, loadInitialData]);

  /**
   * 연결 해제
   */
  const disconnect = useCallback(() => {
    console.log('[Market Data] Disconnect called');
    
    // 재연결 타이머 정리
    if (reconnectTimeoutRef.current) {
      clearTimeout(reconnectTimeoutRef.current);
      reconnectTimeoutRef.current = null;
      console.log('[Market Data] Reconnect timeout cleared');
    }
    
    // WebSocket 연결 정리
    if (wsRef.current) {
      const ws = wsRef.current;
      wsRef.current = null;
      
      try {
        // 구독 해제 메시지
        if (ws.readyState === WebSocket.OPEN) {
          ws.send(JSON.stringify({ 
            type: 'unsubscribe',
            market,
            timeframe
          }));
        }
        
        // 이벤트 핸들러 제거
        ws.onopen = null;
        ws.onmessage = null;
        ws.onerror = null;
        ws.onclose = null;
        
        // 연결 종료
        if (ws.readyState !== WebSocket.CLOSED) {
          ws.close();
        }
        
        console.log('[Market Data] WebSocket cleaned up');
      } catch (error) {
        console.error('[Market Data] Error during disconnect:', error);
      }
    }
    
    setStatus('disconnected');
  }, [market, timeframe]);

  /**
   * Ping 전송
   */
  const sendPing = useCallback(() => {
    if (wsRef.current && wsRef.current.readyState === WebSocket.OPEN) {
      wsRef.current.send(JSON.stringify({ type: 'ping' }));
    }
  }, []);

  // 자동 연결/해제
  useEffect(() => {
    console.log(`🔌 [Market Data useEffect] enabled=${enabled}, market=${market}, timeframe=${timeframe}`);
    console.log(`🔌 [Market Data useEffect] WebSocket URL will be: ${REALTIME_WS_ENDPOINT}`);
    
    // 종목·봉 변경 시 이전 실패 카운트 초기화 (한 번 막히면 영구 "끊김" 방지)
    reconnectAttemptsRef.current = 0;
    if (reconnectTimeoutRef.current) {
      clearTimeout(reconnectTimeoutRef.current);
      reconnectTimeoutRef.current = null;
    }
    
    // ✅ 종목/타임프레임 변경 시 무조건 기존 연결 정리
    disconnect();

    if (enabled) {
      // disconnect()가 동기적으로 disconnected를 넣으므로, 곧 연결 시도할 때는 '연결 중'으로 표시 (잘못된 '연결 끊김' 노출 방지)
      setStatus('connecting');
      console.log('🟢 [Market Data useEffect] Calling connect after cleanup...');
      let cancelled = false;
      // 짧은 딜레이 후 재연결 (disconnect가 완전히 처리되도록). React Strict Mode에서 첫 타이머가 취소돼도 두 번째 마운트에서 연결됨.
      const timer = window.setTimeout(() => {
        if (!cancelled) {
          connect();
        }
      }, 100);
      
      return () => {
        cancelled = true;
        window.clearTimeout(timer);
        console.log('🧹 [Market Data useEffect cleanup]');
        disconnect();
      };
    } else {
      console.log('🔴 [Market Data useEffect] Disabled, staying disconnected');
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [enabled, market, timeframe]);

  // Keepalive (30초마다 ping)
  useEffect(() => {
    if (status !== 'connected') return;

    const interval = setInterval(() => {
      sendPing();
    }, 30000);

    return () => clearInterval(interval);
  }, [status, sendPing]);

  /** 브라우저 탭이 다시 보일 때 소켓이 끊겨 있으면 즉시 재연결 (절전/백그라운드 후 '연결 끊김' 고착 완화) */
  useEffect(() => {
    if (!enabled) return;

    const onVisible = () => {
      if (document.visibilityState !== 'visible') return;
      const w = wsRef.current;
      const dead =
        !w ||
        w.readyState === WebSocket.CLOSING ||
        w.readyState === WebSocket.CLOSED;
      if (dead && reconnectTimeoutRef.current == null) {
        reconnectAttemptsRef.current = 0;
        connect();
      }
    };

    document.addEventListener('visibilitychange', onVisible);
    return () => document.removeEventListener('visibilitychange', onVisible);
  }, [enabled, connect]);

  /** 실제 WebSocket.readyState와 React 상태 불일치 보정 (간헐적 UI '끊김' 표시 완화) */
  useEffect(() => {
    if (!enabled) return;

    const id = window.setInterval(() => {
      const w = wsRef.current;
      if (w?.readyState === WebSocket.OPEN) {
        setStatus((prev) => (prev === 'connected' ? prev : 'connected'));
      }
    }, 3000);

    return () => window.clearInterval(id);
  }, [enabled]);

  // WebSocket 연결 실패 시 REST 폴링 폴백 (SSL 인증서 등으로 WS 차단 시)
  useEffect(() => {
    if (!enabled || status === 'connected' || status === 'connecting') return;
    if (reconnectAttemptsRef.current < 25) return;
    
    console.log('⚠️ [Market Data] WebSocket 연결 실패, REST 폴링 폴백 시작');
    
    const pollInterval = setInterval(() => {
      loadInitialData();
    }, 5000);
    
    return () => clearInterval(pollInterval);
  }, [enabled, status, loadInitialData]);

  return {
    status,
    candles,
    indicators,
    connect,
    disconnect,
  };
};
