export async function streamSseResponse(
  response: Response,
  onMessage: (raw: string) => void,
): Promise<void> {
  const reader = response.body?.getReader();
  if (!reader) return;

  const decoder = new TextDecoder();
  let buffer = "";
  let eventData = "";
  while (true) {
    const { done, value } = await reader.read();
    buffer += decoder.decode(value ?? new Uint8Array(), { stream: !done });
    ({ buffer, eventData } = consumeSseBuffer(buffer, eventData, onMessage));

    if (done) break;
  }

  const tail = `${eventData}\n${buffer}`.trim();
  if (tail) onMessage(tail);
}

function consumeSseBuffer(
  rawBuffer: string,
  rawEventData: string,
  onMessage: (raw: string) => void,
): { buffer: string; eventData: string } {
  let buffer = rawBuffer;
  let eventData = rawEventData;
  let newlineIndex = buffer.indexOf("\n");
  while (newlineIndex >= 0) {
    const line = buffer.slice(0, newlineIndex).replace(/\r$/, "");
    buffer = buffer.slice(newlineIndex + 1);
    eventData = consumeSseLine(line, eventData, onMessage);
    newlineIndex = buffer.indexOf("\n");
  }
  return { buffer, eventData };
}

function consumeSseLine(line: string, eventData: string, onMessage: (raw: string) => void): string {
  if (!line) {
    const payload = eventData.trim();
    if (payload) onMessage(payload);
    return "";
  }
  if (!line.startsWith("data:")) return eventData;
  const chunk = line.slice(5).trimStart();
  return eventData ? `${eventData}\n${chunk}` : chunk;
}
