/**
 * 업비트 실시간 호가 훅
 *
 * frontend_golden/src/hooks/useUpbitOrderbook.ts 를 현재 프로젝트에 맞게 변환:
 *  - WebSocket: /upbit-ws  (nginx 프록시 → wss://api.upbit.com/websocket/v1)
 *  - REST:      /upbit-api/v1/orderbook
 *  - console.log 제거, 재연결 로직 강화
 */

import { useState, useEffect, useRef, useCallback, useMemo } from 'react';

// ── 타입 ────────────────────────────────────────────────────────────────────
export interface OrderbookUnit {
  ask_price: number;
  bid_price: number;
  ask_size:  number;
  bid_size:  number;
}

export interface OrderbookDisplayUnit {
  price:      number;
  size:       number;
  percentage: number;   // 최대 잔량 대비 비율 (0–100), 배경 바 넓이
  type:       'ask' | 'bid';
}

export interface OrderbookState {
  asks:         OrderbookDisplayUnit[];
  bids:         OrderbookDisplayUnit[];
  totalAskSize: number;
  totalBidSize: number;
  timestamp:    number;
  connected:    boolean;
}

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

interface UpbitObWsMessage {
  type:            string;
  code:            string;
  total_ask_size:  number;
  total_bid_size:  number;
  orderbook_units: OrderbookUnit[];
  timestamp:       number;
  stream_type?:    string;
}

// ── 상수 ────────────────────────────────────────────────────────────────────
const RECONNECT_DELAY  = 3_000;
const INITIAL_STATE: OrderbookState = {
  asks: [], bids: [], totalAskSize: 0, totalBidSize: 0, timestamp: 0, connected: false,
};

// ── 훅 ──────────────────────────────────────────────────────────────────────
export function useUpbitOrderbook(market: string) {
  const [orderbook, setOrderbook] = useState<OrderbookState>(INITIAL_STATE);
  const [wsStatus,  setWsStatus]  = useState<WsStatus>('disconnected');

  const wsRef            = useRef<WebSocket | null>(null);
  const reconnTimerRef   = useRef<ReturnType<typeof setTimeout> | null>(null);
  const mountedRef       = useRef(true);
  const isConnectingRef  = useRef(false);

  // ── 데이터 파싱 → state ─────────────────────────────────────────────────
  const processData = useCallback((msg: UpbitObWsMessage) => {
    const units = msg.orderbook_units;
    if (!units || units.length === 0) return;

    const maxSize = Math.max(
      ...units.map(u => u.ask_size),
      ...units.map(u => u.bid_size),
    );

    // 매도: 높은 가격 → 낮은 가격 (역순)
    const asks: OrderbookDisplayUnit[] = units
      .map(u => ({
        price:      u.ask_price,
        size:       u.ask_size,
        percentage: maxSize > 0 ? (u.ask_size / maxSize) * 100 : 0,
        type:       'ask' as const,
      }))
      .reverse();

    // 매수: 높은 가격 → 낮은 가격 (정순 – 1번 unit이 best bid)
    const bids: OrderbookDisplayUnit[] = units.map(u => ({
      price:      u.bid_price,
      size:       u.bid_size,
      percentage: maxSize > 0 ? (u.bid_size / maxSize) * 100 : 0,
      type:       'bid' as const,
    }));

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

  // ── REST 초기 스냅샷 ────────────────────────────────────────────────────
  const fetchSnapshot = useCallback(async () => {
    try {
      const res = await fetch(`/upbit-api/v1/orderbook?markets=${market}`);
      if (!res.ok) return;
      const data: Array<{
        market: string;
        timestamp: number;
        total_ask_size: number;
        total_bid_size: number;
        orderbook_units: OrderbookUnit[];
      }> = await res.json();
      if (data && data[0]) {
        const d = data[0];
        processData({
          type:            'orderbook',
          code:            d.market,
          total_ask_size:  d.total_ask_size,
          total_bid_size:  d.total_bid_size,
          orderbook_units: d.orderbook_units,
          timestamp:       d.timestamp,
          stream_type:     'SNAPSHOT',
        });
      }
    } catch { /* ignore */ }
  }, [market, processData]);

  // ── WebSocket 연결 ──────────────────────────────────────────────────────
  const connect = useCallback(() => {
    if (!mountedRef.current) return;
    if (isConnectingRef.current) return;
    if (wsRef.current?.readyState === WebSocket.OPEN) return;

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

    const proto = window.location.protocol === 'https:' ? 'wss' : 'ws';
    const ws = new WebSocket(`${proto}://${window.location.host}/upbit-ws`);
    ws.binaryType = 'arraybuffer';
    wsRef.current = ws;

    ws.onopen = () => {
      if (!mountedRef.current) { ws.close(); return; }
      isConnectingRef.current = false;
      setWsStatus('connected');
      ws.send(JSON.stringify([
        { ticket: `ob-${market}-${Date.now()}` },
        { type: 'orderbook', codes: [market] },
      ]));
    };

    ws.onmessage = async (e: MessageEvent) => {
      if (!mountedRef.current) return;
      try {
        let text: string;
        if (typeof e.data === 'string') {
          text = e.data;
        } else if (e.data instanceof ArrayBuffer) {
          text = new TextDecoder('utf-8').decode(e.data);
        } else if (e.data instanceof Blob) {
          text = await (e.data as Blob).text();
        } else return;

        const msg: UpbitObWsMessage = JSON.parse(text);
        if (msg.type === 'orderbook' && msg.code === market) {
          processData(msg);
        }
      } catch { /* ignore */ }
    };

    ws.onerror = () => {
      isConnectingRef.current = false;
      if (mountedRef.current) setWsStatus('error');
    };

    ws.onclose = () => {
      isConnectingRef.current = false;
      if (!mountedRef.current) return;
      setWsStatus('disconnected');
      setOrderbook(prev => ({ ...prev, connected: false }));
      // 자동 재연결
      reconnTimerRef.current = setTimeout(connect, RECONNECT_DELAY);
    };
  }, [market, processData]);

  // ── 연결 해제 ────────────────────────────────────────────────────────────
  const disconnect = useCallback(() => {
    if (reconnTimerRef.current) { clearTimeout(reconnTimerRef.current); reconnTimerRef.current = null; }
    if (wsRef.current) {
      const ws = wsRef.current;
      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;
  }, []);

  // ── 라이프사이클 ─────────────────────────────────────────────────────────
  useEffect(() => {
    mountedRef.current = true;
    setOrderbook(INITIAL_STATE);
    fetchSnapshot();
    connect();
    return () => {
      mountedRef.current = false;
      disconnect();
    };
  }, [market]); // eslint-disable-line react-hooks/exhaustive-deps

  // ── 스프레드 계산 ────────────────────────────────────────────────────────
  const spread = useMemo(() => {
    const { asks, bids } = orderbook;
    if (!asks.length || !bids.length) return { bestAsk: 0, bestBid: 0, spread: 0, pct: 0 };
    const bestAsk = asks[asks.length - 1].price; // asks 배열 마지막 = 가장 낮은 매도가
    const bestBid = bids[0].price;               // bids 배열 첫번째 = 가장 높은 매수가
    const diff    = bestAsk - bestBid;
    return { bestAsk, bestBid, spread: diff, pct: bestBid > 0 ? (diff / bestBid) * 100 : 0 };
  }, [orderbook.asks, orderbook.bids]);

  return { orderbook, wsStatus, spread };
}
