import { useEffect, useRef } from "react";

export type EventStreamHandlers = Record<string, (payload: unknown) => void>;

// useEventStream connects to /api/events via EventSource and dispatches
// named events to the matching handler. The handlers object is captured in
// a ref so callers can supply fresh closures every render without tearing
// the connection down. Reconnection is handled by the browser's built-in
// EventSource backoff; the hook only opens one socket per mount.
export function useEventStream(handlers: EventStreamHandlers, enabled = true) {
  const ref = useRef(handlers);
  ref.current = handlers;

  useEffect(() => {
    if (!enabled) return;
    const es = new EventSource("/api/events", { withCredentials: true });
    const listeners: Record<string, (ev: MessageEvent) => void> = {};

    // We attach a listener per event type known when this effect runs.
    // Types added later via handler ref updates are still handled because
    // the inner closure always reads ref.current.
    for (const type of Object.keys(ref.current)) {
      const fn = (ev: MessageEvent) => {
        const cb = ref.current[type];
        if (!cb) return;
        try {
          const payload = ev.data ? JSON.parse(ev.data) : null;
          cb(payload);
        } catch {
          // Malformed payload; ignore.
        }
      };
      es.addEventListener(type, fn);
      listeners[type] = fn;
    }

    return () => {
      for (const [type, fn] of Object.entries(listeners)) {
        es.removeEventListener(type, fn);
      }
      es.close();
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [enabled]);
}
