/**
 * SSE client for POST /api/v1/chat.
 *
 * Uses raw fetch() with ReadableStream to parse Server-Sent Events:
 * - event: sources  — citation metadata array (sent first)
 * - event: token    — streamed text chunk (repeated)
 * - event: done     — completion metadata with cascade_tier
 * - event: error    — error message on failure
 */

import { BASE, AUTH_TOKEN_KEY } from "./client";

export interface ChatSource {
  number: number;
  title: string;
  slug: string;
  creator_name: string;
  topic_category: string;
  summary: string;
  section_anchor: string;
  section_heading: string;
}

export interface ChatCallbacks {
  onSources: (sources: ChatSource[]) => void;
  onToken: (text: string) => void;
  onDone: (meta: { cascade_tier: string }) => void;
  onError: (message: string) => void;
}

/**
 * Stream a chat response from the API via SSE.
 * Returns an AbortController so the caller can cancel.
 */
export function streamChat(
  query: string,
  callbacks: ChatCallbacks,
  creator?: string,
): AbortController {
  const controller = new AbortController();

  const token = (() => {
    try {
      return localStorage.getItem(AUTH_TOKEN_KEY);
    } catch {
      return null;
    }
  })();

  const headers: Record<string, string> = {
    "Content-Type": "application/json",
  };
  if (token) {
    headers["Authorization"] = `Bearer ${token}`;
  }

  fetch(`${BASE}/chat`, {
    method: "POST",
    headers,
    body: JSON.stringify({ query, creator: creator ?? null }),
    signal: controller.signal,
  })
    .then(async (res) => {
      if (!res.ok) {
        let detail = res.statusText;
        try {
          const body = await res.json();
          if (body?.detail) {
            detail =
              typeof body.detail === "string"
                ? body.detail
                : JSON.stringify(body.detail);
          }
        } catch {
          /* body not JSON */
        }
        callbacks.onError(`Request failed: ${detail}`);
        return;
      }

      const reader = res.body?.getReader();
      if (!reader) {
        callbacks.onError("No response body");
        return;
      }

      const decoder = new TextDecoder();
      let buffer = "";

      while (true) {
        const { done, value } = await reader.read();
        if (done) break;

        buffer += decoder.decode(value, { stream: true });

        // Parse complete SSE events (separated by double newline)
        const parts = buffer.split("\n\n");
        // Keep the last incomplete part in the buffer
        buffer = parts.pop() ?? "";

        for (const part of parts) {
          if (!part.trim()) continue;
          const eventMatch = part.match(/^event:\s*(.+)$/m);
          const dataMatch = part.match(/^data:\s*(.+)$/m);
          if (!eventMatch?.[1] || !dataMatch?.[1]) continue;

          const eventType = eventMatch[1].trim();
          const rawData = dataMatch[1];

          try {
            switch (eventType) {
              case "sources":
                callbacks.onSources(JSON.parse(rawData) as ChatSource[]);
                break;
              case "token":
                callbacks.onToken(JSON.parse(rawData) as string);
                break;
              case "done":
                callbacks.onDone(JSON.parse(rawData) as { cascade_tier: string });
                break;
              case "error": {
                const err = JSON.parse(rawData) as { message?: string };
                callbacks.onError(err.message ?? "Unknown error");
                break;
              }
            }
          } catch {
            // Malformed JSON — skip this event
          }
        }
      }
    })
    .catch((err: Error) => {
      if (err.name !== "AbortError") {
        callbacks.onError(err.message || "Network error");
      }
    });

  return controller;
}
