import { useCallback, useEffect, useRef } from 'react'

const INITIAL_RECONNECT_DELAY = 3000
const MAX_RECONNECT_DELAY = 30000

/**
 * Opens an SSE connection to /api/sse/stream?token=<jwt>.
 *
 * Callbacks (onEvent, onConnect, onDisconnect) are stored in refs so they are
 * always current without causing the EventSource to reconnect when they change.
 *
 * The connection is created/destroyed only when `token` or `enabled` changes.
 */
export function useSSE({ token, onEvent, onConnect, onDisconnect, enabled = true }) {
  // Keep callbacks in refs so the EventSource closure always calls the latest version
  const onEventRef = useRef(onEvent)
  const onConnectRef = useRef(onConnect)
  const onDisconnectRef = useRef(onDisconnect)
  useEffect(() => { onEventRef.current = onEvent }, [onEvent])
  useEffect(() => { onConnectRef.current = onConnect }, [onConnect])
  useEffect(() => { onDisconnectRef.current = onDisconnect }, [onDisconnect])

  const esRef = useRef(null)
  const reconnectTimer = useRef(null)
  const reconnectDelay = useRef(INITIAL_RECONNECT_DELAY)
  const unmounted = useRef(false)
  // Expose reconnect so SSEContext can trigger it after heartbeat recovery
  const reconnectRef = useRef(null)

  useEffect(() => {
    if (!token || !enabled) return
    unmounted.current = false

    function connect() {
      if (unmounted.current) return
      if (esRef.current) {
        esRef.current.close()
        esRef.current = null
      }

      const url = `/api/sse/stream?token=${encodeURIComponent(token)}`
      const es = new EventSource(url)
      esRef.current = es

      es.onopen = () => {
        reconnectDelay.current = INITIAL_RECONNECT_DELAY
        onConnectRef.current?.()
      }

      es.onmessage = (e) => {
        try {
          const { type, data } = JSON.parse(e.data)
          onEventRef.current?.(type, data)
        } catch {
          // malformed event — ignore
        }
      }

      es.onerror = () => {
        es.close()
        esRef.current = null
        onDisconnectRef.current?.()
        if (unmounted.current) return
        reconnectTimer.current = setTimeout(() => {
          reconnectDelay.current = Math.min(
            reconnectDelay.current * 1.5,
            MAX_RECONNECT_DELAY
          )
          connect()
        }, reconnectDelay.current)
      }
    }

    reconnectRef.current = connect
    connect()

    return () => {
      unmounted.current = true
      clearTimeout(reconnectTimer.current)
      esRef.current?.close()
      esRef.current = null
    }
  }, [token, enabled])

  // Stable reference — never changes, so heartbeat useEffect dep array stays stable
  const reconnect = useCallback(() => {
    clearTimeout(reconnectTimer.current)
    reconnectDelay.current = INITIAL_RECONNECT_DELAY
    reconnectRef.current?.()
  }, [])

  return { reconnect }
}
