/**
 * WebSocket connection hook with auto-reconnect.
 *
 * Connects to /api/ws?token=<jwt> and emits parsed JSON events via callbacks.
 * Reconnect strategy: 1s, 2s, 4s, 8s, ... capped at 30s.
 */
import { useEffect, useRef, useCallback } from 'react'
import { useAuthStore } from '../store/auth'

export interface WSEvent {
  type: string
  [key: string]: unknown
}

export type WSEventHandler = (event: WSEvent) => void

interface UseWebSocketOptions {
  onEvent?: WSEventHandler
  enabled?: boolean
}

const WS_BASE =
  window.location.protocol === 'https:'
    ? `wss://${window.location.host}`
    : `ws://${window.location.hostname}:8888`

const PING_INTERVAL_MS = 25_000
const MAX_BACKOFF_MS = 30_000

export function useWebSocket({ onEvent, enabled = true }: UseWebSocketOptions = {}) {
  const token = useAuthStore((s) => s.token)
  const wsRef = useRef<WebSocket | null>(null)
  const backoffRef = useRef(1000)
  const pingRef = useRef<ReturnType<typeof setInterval> | null>(null)
  const reconnectRef = useRef<ReturnType<typeof setTimeout> | null>(null)
  const onEventRef = useRef(onEvent)
  onEventRef.current = onEvent

  const cleanup = useCallback(() => {
    if (pingRef.current) clearInterval(pingRef.current)
    if (reconnectRef.current) clearTimeout(reconnectRef.current)
    if (wsRef.current) {
      wsRef.current.onclose = null
      wsRef.current.onerror = null
      wsRef.current.onmessage = null
      wsRef.current.close()
      wsRef.current = null
    }
  }, [])

  const connect = useCallback(() => {
    if (!token || !enabled) return
    cleanup()

    const url = `${WS_BASE}/api/ws?token=${encodeURIComponent(token)}`
    const ws = new WebSocket(url)
    wsRef.current = ws

    ws.onopen = () => {
      backoffRef.current = 1000 // reset backoff on successful connect
      // Keep-alive pings
      pingRef.current = setInterval(() => {
        if (ws.readyState === WebSocket.OPEN) {
          ws.send('ping')
        }
      }, PING_INTERVAL_MS)
    }

    ws.onmessage = (evt) => {
      try {
        const event = JSON.parse(evt.data) as WSEvent
        onEventRef.current?.(event)
      } catch {
        // ignore non-JSON messages (e.g. pong)
      }
    }

    ws.onclose = () => {
      if (pingRef.current) clearInterval(pingRef.current)
      // Schedule reconnect with exponential backoff
      const delay = backoffRef.current
      backoffRef.current = Math.min(backoffRef.current * 2, MAX_BACKOFF_MS)
      reconnectRef.current = setTimeout(() => connect(), delay)
    }

    ws.onerror = () => {
      ws.close()
    }
  }, [token, enabled, cleanup])

  useEffect(() => {
    if (token && enabled) {
      connect()
    } else {
      cleanup()
    }
    return cleanup
  }, [token, enabled, connect, cleanup])
}
