'use client'

import {
  createContext,
  useContext,
  useEffect,
  useMemo,
  useRef,
  useState,
  type ReactNode,
} from 'react'

import type { LiveSnapshot } from '@/lib/services/live-snapshot'

export type LiveStatus = 'connecting' | 'live' | 'reconnecting' | 'error'

interface LiveGridValue {
  snapshot: LiveSnapshot | null
  status: LiveStatus
  lastUpdate: number | null
}

const LiveGridContext = createContext<LiveGridValue>({
  snapshot: null,
  status: 'connecting',
  lastUpdate: null,
})

/**
 * One EventSource for the whole console.
 *
 * Every live tile subscribes through this context rather than opening its own stream —
 * a Command Center with eight live panels would otherwise hold eight connections and
 * recompute the national snapshot eight times per tick.
 *
 * `initial` comes from the server render, so panels have real values on first paint and
 * never flash a skeleton while the stream connects.
 */
export function LiveGridProvider({
  initial,
  children,
  enabled = true,
}: {
  initial: LiveSnapshot | null
  children: ReactNode
  enabled?: boolean
}) {
  const [snapshot, setSnapshot] = useState<LiveSnapshot | null>(initial)
  const [status, setStatus] = useState<LiveStatus>(enabled ? 'connecting' : 'error')
  const [lastUpdate, setLastUpdate] = useState<number | null>(initial ? Date.now() : null)
  const retryRef = useRef(0)

  useEffect(() => {
    if (!enabled) return

    let source: EventSource | null = null
    let retryTimer: number | undefined
    let disposed = false

    const connect = () => {
      if (disposed) return
      source = new EventSource('/api/stream/grid')

      source.addEventListener('open', () => {
        retryRef.current = 0
        setStatus('live')
      })

      source.addEventListener('snapshot', (event) => {
        try {
          setSnapshot(JSON.parse((event as MessageEvent).data) as LiveSnapshot)
          setLastUpdate(Date.now())
          setStatus('live')
        } catch {
          // A malformed frame should not kill the stream; the next tick will be fine.
        }
      })

      source.addEventListener('error', () => {
        source?.close()
        source = null
        if (disposed) return
        setStatus('reconnecting')
        // Exponential backoff, capped — a server restart should not turn into a
        // reconnect storm from every open wall display.
        retryRef.current = Math.min(retryRef.current + 1, 6)
        const delay = Math.min(30_000, 1000 * 2 ** retryRef.current)
        retryTimer = window.setTimeout(connect, delay)
      })
    }

    connect()

    return () => {
      disposed = true
      if (retryTimer) window.clearTimeout(retryTimer)
      source?.close()
    }
  }, [enabled])

  const value = useMemo(() => ({ snapshot, status, lastUpdate }), [snapshot, status, lastUpdate])

  return <LiveGridContext.Provider value={value}>{children}</LiveGridContext.Provider>
}

export function useLiveGrid(): LiveGridValue {
  return useContext(LiveGridContext)
}
