/**
 * Minimal SSE-line iterator for provider responses.
 *
 * Reads a fetch response body as a stream and yields each `data:`
 * payload exactly once. Handles event boundaries (`\n\n`), CRLF,
 * and the "[DONE]" sentinel that OpenAI-style providers emit. Skips
 * comments (lines starting with `:`).
 *
 * Usage:
 *   for await (const data of sseLines(response)) {
 *     if (data === '[DONE]') break;
 *     const evt = JSON.parse(data);
 *     ...
 *   }
 */
export async function* sseLines(
  response: Response,
): AsyncGenerator<string, void, void> {
  if (!response.body) return;
  const reader = response.body.getReader();
  const decoder = new TextDecoder();
  let buffer = '';
  while (true) {
    const { value, done } = await reader.read();
    if (done) break;
    buffer += decoder.decode(value, { stream: true });
    let idx: number;
    while ((idx = buffer.indexOf('\n\n')) >= 0) {
      const event = buffer.slice(0, idx);
      buffer = buffer.slice(idx + 2);
      const dataLines: string[] = [];
      for (const raw of event.split('\n')) {
        const line = raw.replace(/\r$/, '');
        if (!line || line.startsWith(':')) continue;
        if (line.startsWith('data:')) {
          dataLines.push(line.slice(5).trimStart());
        }
      }
      if (dataLines.length > 0) yield dataLines.join('\n');
    }
  }
}

/**
 * NDJSON line iterator (Ollama). Yields each non-empty line as a
 * raw string (not parsed) — caller decides what to do with it.
 */
export async function* ndjsonLines(
  response: Response,
): AsyncGenerator<string, void, void> {
  if (!response.body) return;
  const reader = response.body.getReader();
  const decoder = new TextDecoder();
  let buffer = '';
  while (true) {
    const { value, done } = await reader.read();
    if (done) break;
    buffer += decoder.decode(value, { stream: true });
    let idx: number;
    while ((idx = buffer.indexOf('\n')) >= 0) {
      const line = buffer.slice(0, idx).replace(/\r$/, '');
      buffer = buffer.slice(idx + 1);
      if (line) yield line;
    }
  }
  if (buffer.trim()) yield buffer.trim();
}
