/**
 * useSSE — React hook wrapping SSEManager.
 *
 * Creates an SSEManager when `config` is non-null, calls connect() on mount,
 * registers provided event handlers, and disconnects on unmount or config
 * reference change.  Status is tracked via component state using the public
 * EventSource readyState surface — SSEManager exposes `isConnected()` and
 * the underlying EventSource's onopen/onerror hooks, which we observe through
 * polling on a 200 ms interval (SSEManager has no subscribe-to-status API).
 */
import { useState, useEffect, useRef, useMemo } from 'react'
import { SSEManager } from '../../services/sse'
import type { SSEConfig, SSEEventHandler } from '../../services/sse'

export type { SSEConfig, SSEEventHandler }

export type SSEStatus = 'idle' | 'connecting' | 'open' | 'closed' | 'error'

export interface UseSSEResult {
  manager: SSEManager | null
  status: SSEStatus
}

/**
 * @param config - SSE configuration.  Pass `null` to remain disconnected.
 * @param onEvents - Map of event-type → handler to register on the manager.
 *   Handlers are registered once per manager instance; use stable references
 *   to avoid spurious re-registrations.
 */
export function useSSE(
  config: SSEConfig | null,
  onEvents?: Record<string, SSEEventHandler>
): UseSSEResult {
  const [status, setStatus] = useState<SSEStatus>('idle')
  const handlersRef = useRef(onEvents)

  // Keep handler ref current without re-running the effect.
  handlersRef.current = onEvents

  // Destructure the semantically meaningful fields so the memo depends on
  // primitive values rather than object identity. This prevents a new
  // SSEManager from being created on every render when consumers pass an
  // inline config object (e.g. `useSSE({ url: '/stream' })`).
  const configUrl = config?.url ?? null
  const configReconnect = config?.reconnect
  const configReconnectInterval = config?.reconnectInterval
  const configMaxRetries = config?.maxRetries
  const configWithCredentials = config?.withCredentials
  const configAllowQueryTokenAuth = config?.allowQueryTokenAuth

  const manager = useMemo<SSEManager | null>(
    () => (config !== null ? new SSEManager(config) : null),
    // Intentionally using destructured primitives to avoid object-identity churn.
    // `config` is captured from the enclosing scope, which is the current render's value.
    [
      configUrl,
      configReconnect,
      configReconnectInterval,
      configMaxRetries,
      configWithCredentials,
      configAllowQueryTokenAuth,
    ]
  )

  useEffect(() => {
    if (manager === null) {
      setStatus('idle')
      return
    }

    setStatus('connecting')

    // Register all provided handlers and collect unsubscribe functions.
    const unsubs: Array<() => void> = []
    const currentHandlers = handlersRef.current
    if (currentHandlers) {
      for (const [eventType, handler] of Object.entries(currentHandlers)) {
        const unsub = manager.on(eventType, handler)
        unsubs.push(unsub)
      }
    }

    manager.connect()

    // Poll readyState to derive status — SSEManager has no subscribe API.
    const poll = setInterval(() => {
      if (manager.isConnected()) {
        setStatus('open')
      } else if (manager.isFailed()) {
        setStatus('error')
      } else {
        setStatus('connecting')
      }
    }, 200)

    return () => {
      clearInterval(poll)
      for (const unsub of unsubs) unsub()
      manager.disconnect()
      setStatus('closed')
    }
  }, [manager])

  return { manager, status }
}
