import { useState, useEffect, useRef, useCallback } from 'react'
import type { WsInbound, WsOutbound, ConnectionStatus } from '../types/protocol'

interface UseWebSocketOptions {
  url: string
  onMessage?: (message: WsOutbound) => void
  onConnect?: () => void
  onDisconnect?: () => void
  onError?: (error: Event) => void
  reconnectInterval?: number
  maxReconnectAttempts?: number
}

interface UseWebSocketReturn {
  status: ConnectionStatus
  sendMessage: (message: WsInbound) => boolean
  connect: () => void
  disconnect: () => void
}

export function useWebSocket({
  url,
  onMessage,
  onConnect,
  onDisconnect,
  onError,
  reconnectInterval = 3000,
  maxReconnectAttempts = 5,
}: UseWebSocketOptions): UseWebSocketReturn {
  const [status, setStatus] = useState<ConnectionStatus>('disconnected')
  const wsRef = useRef<WebSocket | null>(null)
  const reconnectAttemptsRef = useRef(0)
  const reconnectTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
  const isManualDisconnectRef = useRef(false)

  const connect = useCallback(() => {
    if (wsRef.current?.readyState === WebSocket.OPEN) {
      return
    }

    isManualDisconnectRef.current = false
    setStatus('connecting')

    try {
      const ws = new WebSocket(url)
      wsRef.current = ws

      ws.onopen = () => {
        setStatus('connected')
        reconnectAttemptsRef.current = 0
        onConnect?.()
      }

      ws.onmessage = (event) => {
        try {
          const message = JSON.parse(event.data) as WsOutbound
          onMessage?.(message)
        } catch (error) {
          console.error('Failed to parse message:', error)
        }
      }

      ws.onerror = (error) => {
        setStatus('error')
        onError?.(error)
      }

      ws.onclose = () => {
        setStatus('disconnected')
        onDisconnect?.()

        // Auto reconnect if not manually disconnected
        if (!isManualDisconnectRef.current && reconnectAttemptsRef.current < maxReconnectAttempts) {
          reconnectAttemptsRef.current += 1
          reconnectTimerRef.current = setTimeout(() => {
            connect()
          }, reconnectInterval)
        }
      }
    } catch (error) {
      setStatus('error')
      console.error('WebSocket connection error:', error)
    }
  }, [url, onMessage, onConnect, onDisconnect, onError, reconnectInterval, maxReconnectAttempts])

  const disconnect = useCallback(() => {
    isManualDisconnectRef.current = true

    if (reconnectTimerRef.current) {
      clearTimeout(reconnectTimerRef.current)
      reconnectTimerRef.current = null
    }

    if (wsRef.current) {
      wsRef.current.close()
      wsRef.current = null
    }

    setStatus('disconnected')
  }, [])

  const sendMessage = useCallback((message: WsInbound): boolean => {
    if (wsRef.current?.readyState === WebSocket.OPEN) {
      wsRef.current.send(JSON.stringify(message))
      return true
    }
    console.warn('WebSocket is not connected')
    return false
  }, [])

  // Auto connect on mount, reconnect when url changes
  const prevUrlRef = useRef(url)

  useEffect(() => {
    // 首次挂载，或者 url 发生了变化，都要重连
    if (prevUrlRef.current !== url) {
      console.log(`WebSocket URL changed: ${prevUrlRef.current} → ${url}`)
      // 先断开旧连接
      disconnect()
      prevUrlRef.current = url
    }

    // 短暂延迟确保旧 socket 已关闭
    const timer = setTimeout(() => {
      connect()
    }, 50)

    return () => {
      clearTimeout(timer)
      disconnect()
    }
  }, [url, connect, disconnect])

  return {
    status,
    sendMessage,
    connect,
    disconnect,
  }
}
