import {
  encodeBasicAuthHeaderValue,
  getActiveBasicAuthCredentials,
} from "@client/api/client";

interface EventSourceSubscriptionHandlers<T> {
  onOpen?: () => void;
  onMessage: (payload: T) => void;
  onError?: () => void;
}

function buildAuthHeaders(): Record<string, string> | null {
  const creds = getActiveBasicAuthCredentials();
  if (!creds) return null;
  return { Authorization: encodeBasicAuthHeaderValue(creds) };
}

/**
 * Consume `data: …` frames from an SSE byte stream (RFC 8895-style `data:` lines).
 * Returns unconsumed tail after the last complete `\n\n` delimiter.
 */
function consumeSseText(
  buffer: string,
  onJsonLine: (line: string) => void,
): string {
  let rest = buffer;
  while (true) {
    const sep = rest.indexOf("\n\n");
    if (sep === -1) return rest;
    const frame = rest.slice(0, sep);
    rest = rest.slice(sep + 2);
    const trimmed = frame.trim();
    if (!trimmed || trimmed.startsWith(":")) continue;
    const dataLines = frame
      .split("\n")
      .filter((line) => line.startsWith("data:"))
      .map((line) => line.replace(/^data:\s?/, "").trim())
      .filter(Boolean);
    if (dataLines.length === 0) continue;
    onJsonLine(dataLines.join("\n"));
  }
}

function subscribeViaFetch<T>(
  url: string,
  handlers: EventSourceSubscriptionHandlers<T>,
  authHeaders: Record<string, string>,
): () => void {
  const abort = new AbortController();
  let cancelled = false;

  const run = async () => {
    try {
      const response = await fetch(url, {
        headers: {
          Accept: "text/event-stream",
          ...authHeaders,
        },
        signal: abort.signal,
        credentials: "same-origin",
      });
      if (!response.ok || !response.body) {
        handlers.onError?.();
        return;
      }
      handlers.onOpen?.();
      const reader = response.body.getReader();
      const decoder = new TextDecoder();
      let buffer = "";
      while (!cancelled) {
        const { done, value } = await reader.read();
        if (done) break;
        buffer += decoder.decode(value, { stream: true });
        buffer = consumeSseText(buffer, (jsonLine) => {
          try {
            handlers.onMessage(JSON.parse(jsonLine) as T);
          } catch {
            // Ignore malformed frames
          }
        });
      }
    } catch {
      if (!cancelled) handlers.onError?.();
    }
  };

  void run();

  return () => {
    cancelled = true;
    abort.abort();
  };
}

export function subscribeToEventSource<T>(
  url: string,
  handlers: EventSourceSubscriptionHandlers<T>,
): () => void {
  const authHeaders = buildAuthHeaders();
  if (authHeaders) {
    return subscribeViaFetch(url, handlers, authHeaders);
  }

  const eventSource = new EventSource(url);

  eventSource.onopen = () => {
    handlers.onOpen?.();
  };

  eventSource.onmessage = (event) => {
    try {
      handlers.onMessage(JSON.parse(event.data) as T);
    } catch {
      // Ignore malformed events to keep stream resilient.
    }
  };

  eventSource.onerror = () => {
    handlers.onError?.();
  };

  return () => {
    eventSource.close();
  };
}
