/**
 * @agentguard/sdk — SSE streaming support.
 *
 * Parses Server-Sent Events from the `/v1/generate/stream` endpoint
 * and yields typed SSEEvent objects.
 */

import type { SSEEvent } from "./types.js";
import { AgentGuardAPIError, AgentGuardConnectionError } from "./errors.js";

/**
 * Parse a single SSE line block into a typed event.
 */
function parseSSEEvent(eventType: string, data: string): SSEEvent | null {
  if (!data) return null;

  try {
    const parsed = JSON.parse(data);

    switch (eventType) {
      case "level_complete":
        return { event: "level_complete", ...parsed } as SSEEvent;
      case "validation":
        return { event: "validation", ...parsed } as SSEEvent;
      case "challenge":
        return { event: "challenge", ...parsed } as SSEEvent;
      case "complete":
        return { event: "complete", ...parsed } as SSEEvent;
      case "error":
        return { event: "error", ...parsed } as SSEEvent;
      default:
        return null;
    }
  } catch {
    return null;
  }
}

/**
 * Consume an SSE response stream and yield typed events.
 *
 * @param response - A `fetch` Response whose body is an SSE stream.
 * @yields SSEEvent objects.
 */
export async function* streamSSE(response: Response): AsyncGenerator<SSEEvent, void, undefined> {
  if (!response.body) {
    throw new AgentGuardAPIError(
      response.status,
      "Response body is null — streaming not supported",
    );
  }

  const reader = response.body.getReader();
  const decoder = new TextDecoder();
  let buffer = "";
  let currentEvent = "";
  let currentData = "";

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

      buffer += decoder.decode(value, { stream: true });
      const lines = buffer.split("\n");
      // Keep last incomplete line in buffer
      buffer = lines.pop() ?? "";

      for (const line of lines) {
        if (line.startsWith("event:")) {
          currentEvent = line.slice(6).trim();
        } else if (line.startsWith("data:")) {
          currentData += line.slice(5).trim();
        } else if (line === "") {
          // Empty line = end of event
          if (currentEvent && currentData) {
            const event = parseSSEEvent(currentEvent, currentData);
            if (event) yield event;
          }
          currentEvent = "";
          currentData = "";
        }
      }
    }

    // Flush remaining
    if (currentEvent && currentData) {
      const event = parseSSEEvent(currentEvent, currentData);
      if (event) yield event;
    }
  } finally {
    reader.releaseLock();
  }
}
