/**
 * Minimal SSE helper — wraps `EventSource` so consumers get typed event
 * callbacks and a single `close()` for cleanup.
 *
 * Used by the upload flow to stream /api/progress/{task_id} events.
 * Kept framework-agnostic so any component can drive it; a thin React
 * hook layer sits on top in lib/api/documents.ts.
 */

export type SSEHandlers<T> = {
  onMessage: (data: T) => void;
  onError?: (err: Event) => void;
  /* Called when the server closes the stream cleanly. EventSource has
   * no native "closed" event, so the backend signals completion via a
   * terminal payload and we close from the onMessage handler — callers
   * can return `true` to trigger this path. */
  onClose?: () => void;
};

export function openSSE<T>(
  url: string,
  { onMessage, onError, onClose }: SSEHandlers<T>,
): () => void {
  const es = new EventSource(url);
  let closed = false;

  const close = () => {
    if (closed) return;
    closed = true;
    es.close();
    onClose?.();
  };

  es.addEventListener("message", (ev) => {
    if (closed) return;
    try {
      const payload = JSON.parse(ev.data) as T;
      onMessage(payload);
    } catch {
      /* backend sends heartbeats as comments — EventSource filters them,
       * so any non-JSON message here is a protocol bug worth ignoring */
    }
  });

  es.addEventListener("error", (ev) => {
    if (closed) return;
    onError?.(ev);
    /* EventSource auto-reconnects; we only close on an explicit terminal
     * payload from the server, not on transient network errors */
  });

  return close;
}
