import { useEffect, useRef, useCallback } from 'react'
import { useCameraStore } from '../store/useCameraStore'
import type { CameraStatus } from '../types'

/** Shape of events sent by the Go SSE hub */
interface SSEEvent {
  type: string
  ts: string
  payload?: unknown
}

/** Connection states for the SSE stream */
export type SSEConnectionState = 'connecting' | 'connected' | 'disconnected' | 'error'

interface UseSSEOptions {
  /** Override the SSE endpoint (defaults to VITE_API_URL + /events/stream) */
  endpoint?: string
}

interface UseSSEReturn {
  /** Current connection state */
  connectionState: SSEConnectionState
  /** Last event timestamp */
  lastEventTs: string | null
  /** Manually close and reopen the connection */
  reconnect: () => void
}

const DEFAULT_ENDPOINT = `${import.meta.env.VITE_API_URL || '/api/v1'}/events/stream`

/**
 * `useSSE` connects to the RemoteRig SSE endpoint at `/api/v1/events/stream`
 * and updates the shared camera store (`useCameraStore`) with live events.
 *
 * Handled event types:
 * - `connected` — initial handshake
 * - `camera_status` — full camera status snapshot
 * - `camera_online` / `camera_offline` — online/offline state change
 * - `recording_event` — recording started or stopped
 *
 * EventSource auto-reconnects natively; the hook also exposes a `reconnect()`
 * helper for manual reconnect.
 */
export function useSSE(opts: UseSSEOptions = {}): UseSSEReturn {
  const endpoint = opts.endpoint ?? DEFAULT_ENDPOINT
  const store = useCameraStore()

  const connectionStateRef = useRef<SSEConnectionState>('disconnected')
  const lastEventTsRef = useRef<string | null>(null)
  const esRef = useRef<EventSource | null>(null)

  // Derive a stable reference for connection state that doesn't trigger re-renders
  // across every event. We return it via a getter callback but the store change
  // re-renders are handled by Zustand selectors in consuming components.

  const handleEvent = useCallback(
    (event: MessageEvent) => {
      try {
        const data: SSEEvent = JSON.parse(event.data)
        lastEventTsRef.current = data.ts

        switch (data.type) {
          case 'connected': {
            // Initial handshake — no state update needed
            connectionStateRef.current = 'connected'
            break
          }

          case 'camera_status': {
            // Full camera status payload — upsert into the store
            const cam = data.payload as CameraStatus | undefined
            if (cam?.camera_id) {
              store.updateCamera(cam)
            }
            break
          }

          case 'camera_online': {
            const pl = data.payload as { camera_id?: string } | undefined
            if (pl?.camera_id) {
              store.setOnline(pl.camera_id)
            }
            break
          }

          case 'camera_offline': {
            const pl = data.payload as { camera_id?: string } | undefined
            if (pl?.camera_id) {
              store.setOffline(pl.camera_id)
            }
            break
          }

          case 'recording_event': {
            const pl = data.payload as
              | { camera_id?: string; recording?: boolean }
              | undefined
            if (pl?.camera_id && typeof pl.recording === 'boolean') {
              store.setRecording(pl.camera_id, pl.recording)
            }
            break
          }

          default:
            // Unknown event type — ignore gracefully
            break
        }
      } catch {
        // Malformed JSON — ignore
      }
    },
    [store],
  )

  const connect = useCallback(() => {
    // Close existing connection if any
    esRef.current?.close()

    connectionStateRef.current = 'connecting'
    const es = new EventSource(endpoint)
    esRef.current = es

    es.onopen = () => {
      connectionStateRef.current = 'connected'
    }

    es.onmessage = handleEvent

    es.onerror = () => {
      connectionStateRef.current =
        es.readyState === EventSource.CLOSED ? 'disconnected' : 'error'
      // EventSource auto-reconnects unless CLOSED; nothing extra needed here
    }
  }, [endpoint, handleEvent])

  const reconnect = useCallback(() => {
    esRef.current?.close()
    connect()
  }, [connect])

  useEffect(() => {
    connect()
    return () => {
      esRef.current?.close()
      connectionStateRef.current = 'disconnected'
    }
  }, [connect])

  return {
    get connectionState() {
      return connectionStateRef.current
    },
    get lastEventTs() {
      return lastEventTsRef.current
    },
    reconnect,
  }
}
