import { useState, useEffect, useRef, useCallback, useMemo } from 'react';
import {
  UpbitOrderbookResponse,
  UpbitOrderbookRestResponse,
  OrderbookState,
  OrderbookDisplayUnit,
  WebSocketStatus,
} from '../types/orderbook';

const UPBIT_WS_URL = 'wss://api.upbit.com/websocket/v1';
const UPBIT_REST_API = 'https://api.upbit.com/v1/orderbook';
const RECONNECT_DELAY = 3000;

/**
 * 업비트 실시간 호가 Hook
 */
export const useUpbitOrderbook = (market: string = 'KRW-BTC') => {
  const [orderbookState, setOrderbookState] = useState<OrderbookState>({
    asks: [],
    bids: [],
    totalAskSize: 0,
    totalBidSize: 0,
    timestamp: 0,
    connected: false,
  });
  
  const [wsStatus, setWsStatus] = useState<WebSocketStatus>('disconnected');
  const wsRef = useRef<WebSocket | null>(null);
  const reconnectTimeoutRef = useRef<NodeJS.Timeout | null>(null);
  const isConnectingRef = useRef(false);

  /**
   * REST API로 초기 스냅샷 가져오기
   */
  const fetchInitialSnapshot = useCallback(async () => {
    try {
      const response = await fetch(`${UPBIT_REST_API}?markets=${market}`);
      if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`);
      }
      const data: UpbitOrderbookRestResponse[] = await response.json();
      
      if (data && data.length > 0) {
        const snapshot = data[0];
        processOrderbookData({
          type: 'orderbook',
          code: snapshot.market,
          total_ask_size: snapshot.total_ask_size,
          total_bid_size: snapshot.total_bid_size,
          orderbook_units: snapshot.orderbook_units,
          timestamp: snapshot.timestamp,
          stream_type: 'SNAPSHOT',
        });
      }
    } catch (error) {
      console.error('[Orderbook] Failed to fetch initial snapshot:', error);
    }
  }, [market]);

  /**
   * 호가 데이터 처리 및 상태 업데이트
   */
  const processOrderbookData = useCallback((data: UpbitOrderbookResponse) => {
    const units = data.orderbook_units;
    if (!units || units.length === 0) return;

    // 최대 잔량 계산 (비율 계산용)
    const maxAskSize = Math.max(...units.map(u => u.ask_size));
    const maxBidSize = Math.max(...units.map(u => u.bid_size));
    const maxSize = Math.max(maxAskSize, maxBidSize);

    // 매도 호가 (역순으로 정렬 - 높은 가격부터)
    const asks: OrderbookDisplayUnit[] = units
      .map(unit => ({
        price: unit.ask_price,
        size: unit.ask_size,
        total: unit.ask_size,
        percentage: maxSize > 0 ? (unit.ask_size / maxSize) * 100 : 0,
        type: 'ask' as const,
      }))
      .reverse();

    // 매수 호가 (정순 - 높은 가격부터)
    const bids: OrderbookDisplayUnit[] = units.map(unit => ({
      price: unit.bid_price,
      size: unit.bid_size,
      total: unit.bid_size,
      percentage: maxSize > 0 ? (unit.bid_size / maxSize) * 100 : 0,
      type: 'bid' as const,
    }));

    setOrderbookState({
      asks,
      bids,
      totalAskSize: data.total_ask_size,
      totalBidSize: data.total_bid_size,
      timestamp: data.timestamp,
      connected: true,
    });
  }, []);

  /**
   * WebSocket 연결
   */
  const connect = useCallback(() => {
    // 이미 연결 중이거나 연결되어 있으면 무시
    if (isConnectingRef.current || (wsRef.current && wsRef.current.readyState === WebSocket.OPEN)) {
      return;
    }

    isConnectingRef.current = true;
    setWsStatus('connecting');

    try {
      const ws = new WebSocket(UPBIT_WS_URL);
      
      // 바이너리 데이터를 ArrayBuffer로 받도록 설정
      ws.binaryType = 'arraybuffer';
      
      ws.onopen = () => {
        console.log('[Orderbook] WebSocket connected');
        isConnectingRef.current = false;
        setWsStatus('connected');
        
        // 구독 메시지 전송
        const subscribeMessage = [
          { ticket: `orderbook-${Date.now()}` },
          {
            type: 'orderbook',
            codes: [market],
            isOnlyRealtime: true,
          },
        ];
        
        ws.send(JSON.stringify(subscribeMessage));
        console.log('[Orderbook] Subscription sent:', market);
      };

      ws.onmessage = async (event: MessageEvent) => {
        try {
          let text: string;
          
          // 데이터 타입에 따라 처리
          if (typeof event.data === 'string') {
            // 이미 문자열인 경우
            console.log('[Orderbook] Received string data');
            text = event.data;
          } else if (event.data instanceof ArrayBuffer) {
            // ArrayBuffer인 경우
            console.log('[Orderbook] Received ArrayBuffer, size:', event.data.byteLength);
            text = new TextDecoder('utf-8').decode(event.data);
          } else if (event.data instanceof Blob) {
            // Blob인 경우
            console.log('[Orderbook] Received Blob, size:', event.data.size);
            text = await event.data.text();
          } else {
            console.error('[Orderbook] Unknown data type:', typeof event.data);
            return;
          }
          
          const data: UpbitOrderbookResponse = JSON.parse(text);
          console.log('[Orderbook] Parsed data:', data.type, data.code, 'units:', data.orderbook_units?.length);
          
          if (data.type === 'orderbook' && data.code === market) {
            processOrderbookData(data);
          }
        } catch (error) {
          console.error('[Orderbook] Failed to parse message:', error);
        }
      };

      ws.onerror = (error) => {
        console.error('[Orderbook] WebSocket error:', error);
        isConnectingRef.current = false;
        setWsStatus('error');
      };

      ws.onclose = () => {
        console.log('[Orderbook] WebSocket closed');
        isConnectingRef.current = false;
        setWsStatus('disconnected');
        setOrderbookState(prev => ({ ...prev, connected: false }));
        
        // 자동 재연결 (3초 후)
        reconnectTimeoutRef.current = setTimeout(() => {
          console.log('[Orderbook] Attempting to reconnect...');
          connect();
        }, RECONNECT_DELAY);
      };

      wsRef.current = ws;
    } catch (error) {
      console.error('[Orderbook] Failed to create WebSocket:', error);
      isConnectingRef.current = false;
      setWsStatus('error');
    }
  }, [market, processOrderbookData]);

  /**
   * WebSocket 연결 해제
   */
  const disconnect = useCallback(() => {
    // 재연결 타이머 제거
    if (reconnectTimeoutRef.current) {
      clearTimeout(reconnectTimeoutRef.current);
      reconnectTimeoutRef.current = null;
    }

    // WebSocket 연결 해제
    if (wsRef.current) {
      const ws = wsRef.current;
      
      // 이벤트 핸들러 제거 (onclose에서 재연결 방지)
      ws.onopen = null;
      ws.onmessage = null;
      ws.onerror = null;
      ws.onclose = null;
      
      if (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING) {
        ws.close();
      }
      
      wsRef.current = null;
    }

    isConnectingRef.current = false;
    setWsStatus('disconnected');
    setOrderbookState(prev => ({ ...prev, connected: false }));
  }, []);

  /**
   * 초기화 및 정리
   */
  useEffect(() => {
    // 초기 스냅샷 가져오기
    fetchInitialSnapshot();
    
    // WebSocket 연결
    connect();

    // Cleanup
    return () => {
      disconnect();
    };
  }, [market]); // market 변경 시 재연결

  /**
   * 최고/최저 매도/매수 호가
   */
  const spread = useMemo(() => {
    if (orderbookState.asks.length === 0 || orderbookState.bids.length === 0) {
      return {
        bestAsk: 0,
        bestBid: 0,
        spread: 0,
        spreadPercentage: 0,
      };
    }

    const bestAsk = orderbookState.asks[orderbookState.asks.length - 1].price; // 가장 낮은 매도가
    const bestBid = orderbookState.bids[0].price; // 가장 높은 매수가
    const spread = bestAsk - bestBid;
    const spreadPercentage = bestBid > 0 ? (spread / bestBid) * 100 : 0;

    return {
      bestAsk,
      bestBid,
      spread,
      spreadPercentage,
    };
  }, [orderbookState.asks, orderbookState.bids]);

  return {
    orderbook: orderbookState,
    wsStatus,
    spread,
    connect,
    disconnect,
  };
};
