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;
}
