/**
 * 멀티차트용 Market Data Context (개선 버전)
 * - 각 unique(market, timeframe) 조합마다 개별 WebSocket 연결
 * - Backend Native WS: /api/ws/realtime (배포 nginx /api 프록시 정렬, 레거시 /ws/realtime도 서버 등록)
 * - 새 봉 감지 로직 추가 (isNewCandle 플래그)
 * - 중복 구독 자동 제거
 */
import React, { createContext, useContext, useState, useCallback, useEffect, useRef, useMemo } from 'react';
import { getApiBaseUrl } from '../services/apiConfig';
import { acquireWsCandleSubscription, releaseWsCandleSubscription } from '../services/upbitWsSubscriptionApi';

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

export interface CandleUpdate {
  timestamp: number;
  open: number;
  high: number;
  low: number;
  close: number;
  volume: number;
}

export type MarketDataCallback = (data: {
  candles: CandleUpdate[];
  indicators: Record<string, number>;
  isUpdate?: boolean;
  isNewCandle?: boolean; // ✅ 새 봉 생성 감지 플래그
}) => void;

interface Subscriber {
  market: string;
  timeframe: string;
  callback: MarketDataCallback;
}

/** WebSocket 인스턴스 관리 */
interface WebSocketInstance {
  key: string; // "market:timeframe"
  market: string;
  timeframe: string;
  ws: WebSocket | null;
  status: ConnectionStatus;
  candles: CandleUpdate[];
  indicators: Record<string, number>;
  subscribers: Set<MarketDataCallback>;
  lastCandleTime: number | null; // 새 봉 감지용
  lastReceivedAt: number;
  reconnectTimeout: NodeJS.Timeout | null;
  reconnectAttempts: number;
}

/**
 * 멀티차트 REST는 ChartItem·CandlestickTestPage가 쓰는 `getApiBaseUrl()`과 동일해야 함.
 * (예: 프론트 :3000 origin과 백엔드 :8083 불일치 시 이전 로직은 초기 realtime 캔들이 빈 채로 남을 수 있음)
 */
function getRealtimeCandlesUrl(market: string, timeframe: string, limit: number): string {
  const apiBase = getApiBaseUrl().replace(/\/+$/, '');
  const m = encodeURIComponent(market);
  const tf = encodeURIComponent(timeframe);
  return `${apiBase}/realtime/candles?market=${m}&timeframe=${tf}&limit=${limit}`;
}

/** WebSocket: /api/ws/realtime — nginx가 /api/* 만 백엔드로 넘기는 배포와 정렬. REACT_APP_WS_URL 우선. */
function getRealtimeWebSocketUrl(): 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) || /\/realtime\/?$/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`;
}

/** 시간봉을 밀리초로 변환 */
function timeframeToIntervalMs(tf: string): number {
  const match = /^(\d+)(m|h|d|w|M)$/.exec((tf || '1m').trim());
  if (!match) return 60_000;
  
  const n = parseInt(match[1], 10);
  const u = match[2];
  
  if (u === 'm') return n * 60_000;
  if (u === 'h') return n * 60 * 60_000;
  if (u === 'd') return n * 24 * 60 * 60_000;
  if (u === 'w') return n * 7 * 24 * 60 * 60_000;
  if (u === 'M') return n * 30 * 24 * 60 * 60_000;
  
  return 60_000;
}

/** timestamp를 epoch ms로 변환 */
function toEpochMs(ts: number | undefined): number {
  if (ts == null || !Number.isFinite(ts)) return NaN;
  return ts > 1e12 ? ts : ts * 1000;
}

/** WebSocket 상태 팝업용 구독별 요약 */
export interface MultiChartConnectionListItem {
  market: string;
  timeframe: string;
  koreanName?: string;
  lastReceivedAt: number;
  candleCount: number;
  status: ConnectionStatus; // ✅ 개별 WebSocket 상태
}

interface MultiChartMarketDataContextType {
  status: ConnectionStatus; // 전체 상태 (모든 연결이 connected면 connected)
  subscribe: (market: string, timeframe: string, callback: MarketDataCallback) => () => void;
  connectionList: MultiChartConnectionListItem[];
  reconnect: () => void;
}

const MultiChartMarketDataContext = createContext<MultiChartMarketDataContextType | undefined>(
  undefined
);

export const useMultiChartMarketData = () => {
  const ctx = useContext(MultiChartMarketDataContext);
  if (!ctx) return null;
  return ctx;
};

interface MultiChartMarketDataProviderProps {
  children: React.ReactNode;
  /** 구독할 마켓 목록 [{market, timeframe, koreanName?}, ...] */
  subscriptions: Array<{ market: string; timeframe: string; koreanName?: string }>;
}

export const MultiChartMarketDataProvider: React.FC<MultiChartMarketDataProviderProps> = ({
  children,
  subscriptions,
}) => {
  const [updateTick, setUpdateTick] = useState(0); // UI 갱신 트리거
  const instancesRef = useRef<Map<string, WebSocketInstance>>(new Map());
  const subscriptionMetaRef = useRef<Map<string, { koreanName?: string }>>(new Map());
  
  const getKey = (market: string, timeframe: string) => `${market}:${timeframe}`;
  
  /** unique 구독 목록 추출 */
  const uniqueSubs = useMemo(() => {
    const map = new Map<string, { market: string; timeframe: string; koreanName?: string }>();
    subscriptions.forEach(s => {
      if (s.market && s.timeframe) {
        const key = getKey(s.market, s.timeframe);
        map.set(key, s);
        subscriptionMetaRef.current.set(key, { koreanName: s.koreanName });
      }
    });
    return Array.from(map.values());
  }, [subscriptions]);
  
  /** WebSocket 인스턴스 생성 */
  const createInstance = useCallback((market: string, timeframe: string): WebSocketInstance => {
    const key = getKey(market, timeframe);
    
    void acquireWsCandleSubscription(market, timeframe);

    const instance: WebSocketInstance = {
      key,
      market,
      timeframe,
      ws: null,
      status: 'disconnected',
      candles: [],
      indicators: {},
      subscribers: new Set(),
      lastCandleTime: null,
      lastReceivedAt: 0,
      reconnectTimeout: null,
      reconnectAttempts: 0,
    };
    
    /** 초기 캔들: WebSocket 성공 여부와 무관하게 먼저 REST로 채움 (WS 실패 시에도 멀티차트 실시간 버퍼가 투자분석 REST와 동일 출처를 쓰도록) */
    const loadInitialData = async (inst: WebSocketInstance) => {
      try {
        const url = getRealtimeCandlesUrl(inst.market, inst.timeframe, 200);
        const response = await fetch(url);

        if (!response.ok) throw new Error(`HTTP ${response.status}`);

        const result = await response.json();

        // 너무 적은 봉만으로 스냅샷 콜백하면 멀티차트가 1~수 봉으로 멈출 수 있음(히스토리는 /pattern/candles가 담당)
        const MIN_REALTIME_INITIAL = 20;
        if (result.success && result.data && result.data.length > 0) {
          if (result.data.length < MIN_REALTIME_INITIAL) {
            console.warn(
              `[MultiChart ${inst.key}] 실시간 REST 캔들 ${result.data.length}개(최소 ${MIN_REALTIME_INITIAL} 권장) — 초기 콜백 생략, 패턴/차트 API 우선`
            );
            return;
          }
          const candlesData: CandleUpdate[] = result.data.map((item: any) => ({
            timestamp: item.timestamp,
            open: item.open,
            high: item.high,
            low: item.low,
            close: item.close,
            volume: item.volume,
          }));

          inst.candles = candlesData;

          const lastIndicators = result.data[result.data.length - 1].indicators || {};
          inst.indicators = lastIndicators;
          inst.lastReceivedAt = Date.now();
          // REST 스냅샷의 마지막 봉 시각을 시드하지 않으면, 첫 WS 틱에서 isNewCandle이 항상 false가 되어
          // 차트(REST 마지막 봉)와 WS(이미 다음 봉)가 어긋날 때 updateLastCandle이 무시되고 타이틀만 깜빡일 수 있음
          const lastTs = candlesData[candlesData.length - 1]?.timestamp;
          inst.lastCandleTime = lastTs != null && Number.isFinite(Number(lastTs)) ? lastTs : inst.lastCandleTime;

          inst.subscribers.forEach((callback) => {
            callback({
              candles: inst.candles,
              indicators: inst.indicators,
              isUpdate: false,
            });
          });

          console.log(`[MultiChart ${inst.key}] 초기 데이터 로드: ${candlesData.length}개 캔들`);
          setUpdateTick((t) => t + 1);
        }
      } catch (error) {
        console.error(`[MultiChart ${inst.key}] 초기 데이터 로드 실패:`, error);
      }
    };

    const connect = () => {
      if (instance.ws?.readyState === WebSocket.CONNECTING || instance.ws?.readyState === WebSocket.OPEN) {
        console.log(`🔌 [MultiChart ${key}] 이미 연결 중이거나 연결됨`);
        return;
      }

      void loadInitialData(instance);

      const wsEndpoint = getRealtimeWebSocketUrl();
      console.log(`🔌 [MultiChart ${key}] 연결 시도: ${wsEndpoint}`);
      instance.status = 'connecting';
      setUpdateTick(t => t + 1);
      
      const ws = new WebSocket(wsEndpoint);
      
      ws.onopen = () => {
        console.log(`✅ [MultiChart ${key}] WebSocket 연결 성공`);
        instance.status = 'connected';
        instance.reconnectAttempts = 0;
        setUpdateTick(t => t + 1);
        
        // 구독 요청 (투자분석과 동일한 형식)
        ws.send(JSON.stringify({
          type: 'subscribe',
          market,
          timeframe,
        }));
        
        console.log(`📡 [MultiChart ${key}] 구독 요청 전송`);
      };
      
      ws.onmessage = (event) => {
        try {
          const data = JSON.parse(event.data);
          
          if (data.type === 'connected') {
            console.log(`[MultiChart ${key}] 연결 확인: ${data.sessionId}`);
            instance.status = 'connected';
            setUpdateTick(t => t + 1);
          }
          else if (data.type === 'subscribed') {
            console.log(`[MultiChart ${key}] 구독 확인`);
            instance.status = 'connected';
            setUpdateTick(t => t + 1);
          }
          else if (data.type === 'update' && data.candle) {
            const backendCandle = data.candle;
            const newCandle: CandleUpdate = {
              timestamp: backendCandle.time,
              open: backendCandle.open,
              high: backendCandle.high,
              low: backendCandle.low,
              close: backendCandle.close,
              volume: backendCandle.volume,
            };
            const newIndicators = data.indicators || {};
            
            // ✅ 새 봉 감지: 직전 WS 시각뿐 아니라 REST로 채워진 버퍼 마지막 봉과도 비교 (첫 메시지·재연결 직후 포함)
            const prevAnchor =
              instance.lastCandleTime != null
                ? instance.lastCandleTime
                : instance.candles.length > 0
                  ? instance.candles[instance.candles.length - 1].timestamp
                  : null;

            let isNewCandle = false;
            if (prevAnchor == null && instance.candles.length === 0) {
              isNewCandle = true;
            } else if (prevAnchor != null) {
              const intervalMs = timeframeToIntervalMs(timeframe);
              const lastMs = toEpochMs(prevAnchor);
              const newMs = toEpochMs(newCandle.timestamp);

              if (Number.isFinite(lastMs) && Number.isFinite(newMs)) {
                const lastBucket = Math.floor(lastMs / intervalMs);
                const newBucket = Math.floor(newMs / intervalMs);
                isNewCandle = newBucket !== lastBucket;
              }
            }
            instance.lastCandleTime = newCandle.timestamp;
            
            // candles 버퍼 업데이트
            if (instance.candles.length === 0) {
              instance.candles = [newCandle];
            } else if (instance.candles[instance.candles.length - 1].timestamp === newCandle.timestamp) {
              // 동일 봉 업데이트
              instance.candles[instance.candles.length - 1] = newCandle;
            } else {
              // 새 봉 추가
              instance.candles = [...instance.candles.slice(-199), newCandle];
            }
            
            instance.indicators = newIndicators;
            instance.lastReceivedAt = Date.now();
            
            console.log(`📊 [MultiChart ${key}] 업데이트: close=${newCandle.close}, isNewCandle=${isNewCandle}`);
            
            // 모든 구독자에게 전파
            instance.subscribers.forEach(callback => {
              callback({
                candles: instance.candles,
                indicators: instance.indicators,
                isUpdate: true,
                isNewCandle, // ✅ 새 봉 생성 플래그
              });
            });
            
            setUpdateTick(t => t + 1);
          }
        } catch (error) {
          console.error(`[MultiChart ${key}] 메시지 파싱 에러:`, error);
        }
      };
      
      ws.onerror = (error) => {
        console.error(`❌ [MultiChart ${key}] WebSocket 에러:`, error);
        instance.status = 'error';
        setUpdateTick(t => t + 1);
      };
      
      ws.onclose = (event) => {
        console.log(`🔌 [MultiChart ${key}] 연결 종료: Code ${event.code}`);
        instance.ws = null;
        instance.status = 'disconnected';
        setUpdateTick(t => t + 1);
        
        // 자동 재연결
        const MAX_RETRIES = 25;
        if (instance.reconnectAttempts < MAX_RETRIES && !instance.reconnectTimeout) {
          const delay = Math.min(1000 * Math.pow(2, Math.min(instance.reconnectAttempts, 6)), 30000);
          instance.reconnectAttempts += 1;
          
          console.log(`🔄 [MultiChart ${key}] 재연결 시도 #${instance.reconnectAttempts}/${MAX_RETRIES} (${delay}ms 후)`);
          
          instance.reconnectTimeout = setTimeout(() => {
            instance.reconnectTimeout = null;
            connect();
          }, delay);
        }
      };
      
      instance.ws = ws;
    };
    
    // 즉시 연결 시작
    connect();
    
    return instance;
  }, []);
  
  /** 인스턴스 정리 */
  const destroyInstance = useCallback((instance: WebSocketInstance) => {
    console.log(`🧹 [MultiChart ${instance.key}] 인스턴스 정리`);

    void releaseWsCandleSubscription(instance.market, instance.timeframe);

    if (instance.reconnectTimeout) {
      clearTimeout(instance.reconnectTimeout);
      instance.reconnectTimeout = null;
    }
    
    if (instance.ws) {
      try {
        if (instance.ws.readyState === WebSocket.OPEN) {
          instance.ws.send(JSON.stringify({
            type: 'unsubscribe',
            market: instance.market,
            timeframe: instance.timeframe,
          }));
        }
        
        instance.ws.onopen = null;
        instance.ws.onmessage = null;
        instance.ws.onerror = null;
        instance.ws.onclose = null;
        
        if (instance.ws.readyState !== WebSocket.CLOSED) {
          instance.ws.close();
        }
      } catch (error) {
        console.error(`[MultiChart ${instance.key}] 정리 중 에러:`, error);
      }
      
      instance.ws = null;
    }
    
    instance.subscribers.clear();
    instance.status = 'disconnected';
  }, []);
  
  /** unique 구독 목록이 변경될 때 인스턴스 생성/제거 */
  useEffect(() => {
    const nextInstances = new Map<string, WebSocketInstance>();
    const prevInstances = new Map(instancesRef.current);
    
    // 필요한 인스턴스 생성 또는 재사용
    uniqueSubs.forEach(sub => {
      const key = getKey(sub.market, sub.timeframe);
      
      // 기존 인스턴스 재사용
      if (prevInstances.has(key)) {
        const existing = prevInstances.get(key)!;
        nextInstances.set(key, existing);
        prevInstances.delete(key);
        return;
      }
      
      // 새 인스턴스 생성
      const newInstance = createInstance(sub.market, sub.timeframe);
      nextInstances.set(key, newInstance);
    });
    
    // 사용하지 않는 인스턴스 정리
    prevInstances.forEach(inst => destroyInstance(inst));
    
    instancesRef.current = nextInstances;
    setUpdateTick(t => t + 1);
    
    return () => {
      // cleanup: 모든 인스턴스 정리
      nextInstances.forEach(inst => destroyInstance(inst));
      instancesRef.current.clear();
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [uniqueSubs]); // ✅ createInstance, destroyInstance 의존성 제거 (무한 루프 방지)
  
  /** subscribe 함수: 콜백 등록 및 현재 데이터 즉시 반환 */
  const subscribe = useCallback((market: string, timeframe: string, callback: MarketDataCallback) => {
    const key = getKey(market, timeframe);
    const instance = instancesRef.current.get(key);
    
    if (!instance) {
      console.warn(`[MultiChart] subscribe: ${key} 인스턴스가 없습니다`);
      return () => {};
    }
    
    instance.subscribers.add(callback);
    
    // 현재 캔들 데이터가 있으면 즉시 초기화
    if (instance.candles.length > 0) {
      queueMicrotask(() => {
        callback({
          candles: instance.candles,
          indicators: instance.indicators,
          isUpdate: false,
        });
      });
    }
    
    console.log(`[MultiChart] subscribe: ${key}, 구독자 수=${instance.subscribers.size}`);
    
    return () => {
      instance.subscribers.delete(callback);
      console.log(`[MultiChart] unsubscribe: ${key}, 구독자 수=${instance.subscribers.size}`);
    };
  }, []);
  
  /** 전체 재연결 */
  const reconnect = useCallback(() => {
    console.log('[MultiChart] 전체 재연결 시작');
    instancesRef.current.forEach(instance => {
      instance.reconnectAttempts = 0;
      if (instance.reconnectTimeout) {
        clearTimeout(instance.reconnectTimeout);
        instance.reconnectTimeout = null;
      }
      destroyInstance(instance);
    });
    instancesRef.current.clear();
    setUpdateTick(t => t + 1);
    
    // 짧은 딜레이 후 재생성
    setTimeout(() => {
      uniqueSubs.forEach(sub => {
        const key = getKey(sub.market, sub.timeframe);
        const newInstance = createInstance(sub.market, sub.timeframe);
        instancesRef.current.set(key, newInstance);
      });
      setUpdateTick(t => t + 1);
    }, 100);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [uniqueSubs]); // ✅ createInstance, destroyInstance 의존성 제거
  
  /** 전체 상태 계산 (모든 인스턴스가 connected면 connected) */
  const overallStatus = useMemo((): ConnectionStatus => {
    const instances = Array.from(instancesRef.current.values());
    if (instances.length === 0) return 'disconnected';
    
    const hasError = instances.some(i => i.status === 'error');
    if (hasError) return 'error';
    
    const allConnected = instances.every(i => i.status === 'connected');
    if (allConnected) return 'connected';
    
    const someConnecting = instances.some(i => i.status === 'connecting');
    if (someConnecting) return 'connecting';
    
    return 'disconnected';
  }, [updateTick]);
  
  /** connectionList 생성 */
  const connectionList = useMemo((): MultiChartConnectionListItem[] => {
    return uniqueSubs.map(sub => {
      const key = getKey(sub.market, sub.timeframe);
      const instance = instancesRef.current.get(key);
      const meta = subscriptionMetaRef.current.get(key);
      
      return {
        market: sub.market,
        timeframe: sub.timeframe,
        koreanName: meta?.koreanName || sub.koreanName,
        lastReceivedAt: instance?.lastReceivedAt ?? 0,
        candleCount: instance?.candles.length ?? 0,
        status: instance?.status ?? 'disconnected',
      };
    });
  }, [uniqueSubs, updateTick]);
  
  const value: MultiChartMarketDataContextType = {
    status: overallStatus,
    subscribe,
    connectionList,
    reconnect,
  };
  
  return (
    <MultiChartMarketDataContext.Provider value={value}>
      {children}
    </MultiChartMarketDataContext.Provider>
  );
};
