/**
 * Server-Sent Events parser and terminal renderer for cloud task output.
 *
 * Used by `agents cloud logs -f` to stream live output from a running task
 * and by the post-dispatch follow mode to show progress inline.
 */
import chalk from 'chalk';
/**
 * Translate a (server-emitted SSE event name, raw data string) pair into a
 * typed CloudEvent. Unknown event names are surfaced as `{ type: 'unknown' }`
 * rather than dropped — that's the whole point of widening the taxonomy.
 */
function decodeSSEFrame(name, data, timestamp) {
    let parsed = {};
    try {
        parsed = data ? JSON.parse(data) : {};
    }
    catch {
        // non-JSON payloads are kept as raw text below
    }
    switch (name) {
        case 'status':
            return { type: 'status', status: parsed.status ?? 'running', timestamp };
        case 'text':
        case 'output':
        case 'message':
            return { type: 'text', content: parsed.content ?? data, timestamp };
        case 'thinking':
            return { type: 'thinking', content: parsed.content ?? data, timestamp };
        case 'tool_use':
            return { type: 'tool_use', tool: parsed.tool ?? '', input: parsed.input, timestamp };
        case 'tool_result':
            return { type: 'tool_result', tool: parsed.tool ?? '', output: parsed.output, timestamp };
        case 'usage':
            return {
                type: 'usage',
                model: parsed.model,
                inputTokens: parsed.inputTokens,
                outputTokens: parsed.outputTokens,
                timestamp,
            };
        case 'done':
            return {
                type: 'done',
                status: parsed.status,
                prUrl: parsed.prUrl,
                summary: typeof parsed.output === 'string' ? parsed.output.slice(0, 2000) : parsed.summary,
                timestamp,
            };
        case 'error':
            return { type: 'error', message: parsed.message ?? data, timestamp };
        default:
            return { type: 'unknown', name, data, timestamp };
    }
}
/**
 * Parse a Server-Sent Events stream into CloudEvents.
 * Handles `event:`, `data:`, keepalive comments, and multi-line data.
 */
export async function* parseSSE(response) {
    if (!response.body)
        return;
    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');
            buffer = lines.pop() ?? '';
            for (const line of lines) {
                // Keepalive comment
                if (line.startsWith(':'))
                    continue;
                if (line.startsWith('event: ')) {
                    currentEvent = line.slice(7).trim();
                }
                else if (line.startsWith('data: ')) {
                    currentData += (currentData ? '\n' : '') + line.slice(6);
                }
                else if (line === '') {
                    // Empty line = end of event
                    if (currentEvent || currentData) {
                        yield decodeSSEFrame(currentEvent || 'output', currentData, new Date().toISOString());
                        currentEvent = '';
                        currentData = '';
                    }
                }
            }
        }
        // Flush remaining
        if (currentEvent || currentData) {
            yield decodeSSEFrame(currentEvent || 'output', currentData, new Date().toISOString());
        }
    }
    finally {
        reader.releaseLock();
    }
}
/**
 * Render a stream of CloudEvents to the terminal.
 * Returns the final status and summary when the stream ends.
 */
export async function renderStream(events, options) {
    let lastStatus = 'running';
    let summary;
    let prUrl;
    for await (const event of events) {
        if (options?.json) {
            process.stdout.write(JSON.stringify(event) + '\n');
            continue;
        }
        switch (event.type) {
            case 'status': {
                lastStatus = event.status;
                process.stderr.write(`${statusLabel(lastStatus)}\n`);
                break;
            }
            case 'text': {
                process.stdout.write(event.content);
                break;
            }
            case 'thinking': {
                process.stderr.write(chalk.dim(`[thinking] ${event.content}\n`));
                break;
            }
            case 'tool_use': {
                process.stderr.write(chalk.cyan(`[tool] ${event.tool}\n`));
                break;
            }
            case 'tool_result': {
                // Tool results are usually verbose; just acknowledge inline.
                process.stderr.write(chalk.dim(`[tool_result] ${event.tool}\n`));
                break;
            }
            case 'usage': {
                const parts = [];
                if (event.model)
                    parts.push(event.model);
                if (event.inputTokens != null)
                    parts.push(`in=${event.inputTokens}`);
                if (event.outputTokens != null)
                    parts.push(`out=${event.outputTokens}`);
                if (parts.length > 0) {
                    process.stderr.write(chalk.dim(`[usage] ${parts.join(' ')}\n`));
                }
                break;
            }
            case 'done': {
                lastStatus = event.status ?? 'completed';
                summary = event.summary;
                prUrl = event.prUrl;
                process.stderr.write(`\n${statusLabel(lastStatus)}\n`);
                break;
            }
            case 'error': {
                process.stderr.write(chalk.red(`Error: ${event.message}\n`));
                lastStatus = 'failed';
                break;
            }
            case 'unknown': {
                process.stderr.write(chalk.dim(`[${event.name}] ${event.data}\n`));
                break;
            }
        }
    }
    return { status: lastStatus, summary, prUrl };
}
/** Map a task status string to a colored terminal label. */
function statusLabel(status) {
    switch (status) {
        case 'queued':
        case 'allocating':
            return chalk.blue(`[${status}]`);
        case 'running':
            return chalk.yellow(`[${status}]`);
        case 'idle':
            return chalk.dim('[idle]');
        case 'completed':
            return chalk.green('[completed]');
        case 'needs_review':
        case 'input_required':
            return chalk.magenta('[needs review]');
        case 'failed':
            return chalk.red('[failed]');
        case 'cancelled':
            return chalk.gray('[cancelled]');
        default:
            return chalk.dim(`[${status}]`);
    }
}
