import type { ProviderEvent } from "@dispatch/kernel";

interface ToolCallAccumulator {
  id: string;
  name: string;
  arguments: string;
}

interface SSEChunkDelta {
  content?: string;
  reasoning_content?: string;
  tool_calls?: Array<{
    index: number;
    id?: string;
    function?: { name?: string; arguments?: string };
  }>;
}

interface SSEChunkChoice {
  delta: SSEChunkDelta;
  finish_reason?: string | null;
  index: number;
}

interface SSEChunkUsageDetails {
  cached_tokens?: number;
}

interface SSEChunk {
  id?: string;
  choices?: SSEChunkChoice[];
  usage?: {
    prompt_tokens?: number;
    completion_tokens?: number;
    cache_read_tokens?: number;
    cache_write_tokens?: number;
    prompt_tokens_details?: SSEChunkUsageDetails;
    completion_tokens_details?: Record<string, unknown>;
  };
}

export function parseSSELines(lines: readonly string[]): ProviderEvent[] {
  const events: ProviderEvent[] = [];
  const toolCalls = new Map<number, ToolCallAccumulator>();

  for (const line of lines) {
    const trimmed = line.trim();
    if (!trimmed.startsWith("data:")) continue;

    const data = trimmed.slice(5).trim();
    if (data === "[DONE]") break;

    let chunk: SSEChunk;
    try {
      chunk = JSON.parse(data) as SSEChunk;
    } catch {
      events.push({ type: "error", message: `Invalid JSON in SSE data: ${data}` });
      continue;
    }

    if (chunk.choices) {
      for (const choice of chunk.choices) {
        const delta = choice.delta;

        if (delta.content) {
          events.push({ type: "text-delta", delta: delta.content });
        }

        if (delta.reasoning_content) {
          events.push({ type: "reasoning-delta", delta: delta.reasoning_content });
        }

        if (delta.tool_calls) {
          for (const tc of delta.tool_calls) {
            const existing = toolCalls.get(tc.index);
            if (existing) {
              if (tc.function?.arguments) {
                existing.arguments += tc.function.arguments;
              }
            } else {
              toolCalls.set(tc.index, {
                id: tc.id ?? "",
                name: tc.function?.name ?? "",
                arguments: tc.function?.arguments ?? "",
              });
            }
          }
        }

        if (choice.finish_reason) {
          const sortedIndices = [...toolCalls.keys()].sort((a, b) => a - b);
          for (const idx of sortedIndices) {
            const acc = toolCalls.get(idx);
            if (!acc) continue;
            let input: unknown;
            try {
              input = JSON.parse(acc.arguments);
            } catch {
              input = acc.arguments;
            }
            events.push({
              type: "tool-call",
              toolCallId: acc.id,
              toolName: acc.name,
              input,
            });
          }
          events.push({ type: "finish", reason: choice.finish_reason });
        }
      }
    }

    if (chunk.usage) {
      const cacheRead =
        chunk.usage.cache_read_tokens ?? chunk.usage.prompt_tokens_details?.cached_tokens;
      const cacheWrite = chunk.usage.cache_write_tokens;
      events.push({
        type: "usage",
        usage: {
          inputTokens: chunk.usage.prompt_tokens ?? 0,
          outputTokens: chunk.usage.completion_tokens ?? 0,
          ...(cacheRead !== undefined ? { cacheReadTokens: cacheRead } : {}),
          ...(cacheWrite !== undefined ? { cacheWriteTokens: cacheWrite } : {}),
        },
      });
    }
  }

  return events;
}
