import { useEffect, useState } from 'react';
import type { AgentRun, RunEvent } from '../../lib/orchestratorApi';
import type { UseOrchestrator } from './useOrchestrator';

/**
 * Live event stream for a single agent run, owned locally by the run console
 * (`AgentRunBar`).
 *
 * Keeping the stream — and the event log it accumulates — here, rather than in
 * the board-level `useOrchestrator`, means a burst of streamed events re-renders
 * ONLY the console. The board page and card modal are untouched by per-event
 * updates, which is what eliminates the frame drops at run start.
 *
 * Lifecycle events (status / bevy / done) are reflected back into the shared
 * run registry via `patchRun` / `reflectBevy`, so the board's active indicators
 * and the run record stay correct. The server replays persisted history first
 * (`since=0`), so opening a modal shows the run's prior activity; duplicates are
 * de-duped by `seq`.
 *
 * Returns the ordered events for `runId` (empty while `runId` is null).
 */
export function useRunStream(runId: string | null, orch: UseOrchestrator): RunEvent[] {
  // Pull the two reflectors out so the effect can depend on THESE stable
  // callbacks (each is `useCallback(..., [])`) rather than the whole `orch`
  // object — whose identity changes on every registry mutation. Depending on
  // `orch` directly would tear down + reopen the EventSource (and wipe the log)
  // on every status/bevy/done event, looping forever. See `patchRun`/
  // `reflectBevy` in `useOrchestrator`.
  const { patchRun, reflectBevy } = orch;
  const [events, setEvents] = useState<RunEvent[]>([]);

  useEffect(() => {
    // Reset when switching runs (or closing the console).
    setEvents([]);
    if (!runId) return;

    const es = new EventSource(`/api/orchestrator/runs/${runId}/stream?since=0`);

    const onEvent = (msg: Event) => {
      const ev = JSON.parse((msg as MessageEvent).data) as RunEvent;
      setEvents((prev) => {
        // Dedup by seq (history replay vs. live may overlap).
        if (typeof ev.seq === 'number' && prev.some((e) => e.seq === ev.seq)) return prev;
        return [...prev, ev];
      });

      // Reflect lifecycle events back to the shared registry.
      if (ev.type === 'status') {
        const status = ev.data.status as AgentRun['status'];
        const settling = ['completed', 'failed', 'stopped'].includes(status);
        patchRun(runId, settling ? { status, finishedAt: new Date().toISOString() } : { status });
      } else if (ev.type === 'done' && typeof ev.data.summary === 'string') {
        patchRun(runId, { summary: ev.data.summary });
      } else if (ev.type === 'bevy') {
        const phase = ev.data.phase;
        if (phase === 'start') reflectBevy(runId, true);
        else if (phase === 'end') reflectBevy(runId, false);
      }
    };

    es.addEventListener('event', onEvent);
    // EventSource auto-reconnects on error; nothing to do on onerror.

    return () => {
      es.removeEventListener('event', onEvent);
      es.close();
    };
  }, [runId, patchRun, reflectBevy]);

  return events;
}
