/**
 * Claude Code stream-json 输出解析器
 * 将 Claude Code 的 JSON 流输出转换为标准化日志
 */
// Debug 日志开关
const DEBUG_PARSER = process.env.DEBUG_PARSER === 'true';
import { createAssistantMessage, createToolUse, createThinking, createErrorMessage, createTokenUsageInfo, } from './types.js';
import { addNormalizedEntry, replaceNormalizedEntry, updateEntryContent, updateToolStatus, setSessionId, } from './utils/patch.js';
import { stripAnsiSequences } from './utils/ansi.js';
/**
 * 工具名称到动作类型的映射
 */
function toolNameToAction(toolName) {
    const mapping = {
        Read: 'file_read',
        Edit: 'file_edit',
        Write: 'file_edit',
        Bash: 'command_run',
        Grep: 'search',
        Glob: 'search',
        WebFetch: 'web_fetch',
        Task: 'task_create',
        TodoWrite: 'todo_management',
    };
    return mapping[toolName] || 'tool';
}
/**
 * Claude Code 解析器
 */
export class ClaudeCodeParser {
    msgStore;
    buffer = '';
    toolEntryMap = new Map(); // tool_use_id -> entry index
    currentAssistantIndex = null;
    currentAssistantContent = '';
    // stream_event state
    streamingBlocks = new Map();
    streamingRole = null;
    // message_id → StreamingMessageState，用于 assistant 消息的 replace 机制
    streamingMessages = new Map();
    streamingMessageId = null;
    // 最后一轮 assistant 消息的 per-turn usage（代表当前上下文窗口占用量）
    lastTurnUsage = null;
    // 是否解析到了有效的 JSON 消息
    hasValidMessages = false;
    // 收集非 JSON 输出行（用于进程异常退出时展示错误）
    nonJsonLines = [];
    // API 重试条目的 entry index（用于 replace 更新，避免刷屏）
    apiRetryEntryIndex = null;
    apiRetryEntryId = null;
    constructor(msgStore) {
        this.msgStore = msgStore;
    }
    /** 使用 MsgStore 共享的索引提供器 */
    get indexProvider() {
        return this.msgStore.entryIndex;
    }
    /**
     * 处理原始输出数据
     */
    processData(data) {
        const now = Date.now();
        if (DEBUG_PARSER) {
            console.log(`[Parser:processData] t=${now} dataLen=${data.length} bufferLen=${this.buffer.length}`);
        }
        // Strip ANSI escape sequences before buffering. Windows ConPTY injects
        // cursor-control / screen-clear / OSC title sequences into the data
        // stream that would corrupt JSON parsing.
        const cleaned = stripAnsiSequences(data);
        this.buffer += cleaned;
        const lines = this.buffer.split('\n');
        this.buffer = lines.pop() || '';
        if (DEBUG_PARSER && lines.length > 0) {
            console.log(`[Parser:processData] t=${now} linesCount=${lines.length} remainingBuffer=${this.buffer.length}`);
        }
        for (const line of lines) {
            if (line.trim()) {
                this.parseLine(line);
            }
        }
    }
    /**
     * 解析单行 JSON
     */
    parseLine(line) {
        const now = Date.now();
        try {
            const msg = JSON.parse(line);
            if (DEBUG_PARSER) {
                console.log(`[Parser:parseLine] t=${now} type=${msg.type} subtype=${msg.subtype || '-'}`);
            }
            this.hasValidMessages = true;
            this.handleMessage(msg);
        }
        catch {
            // 非 JSON 行 — 剥离 ANSI 转义序列后收集，用于进程异常退出时展示
            const stripped = stripAnsiSequences(line).trim();
            // 过滤掉 PTY 初始化残留的单字符噪音（如 `u` 来自 \x1b[?u 键盘协议查询被分块传输）
            if (stripped.length >= 4) {
                this.nonJsonLines.push(stripped);
            }
            if (DEBUG_PARSER && line.length > 0) {
                console.log(`[Parser:parseLine] t=${now} non-JSON line len=${line.length} stripped="${stripped.slice(0, 50)}${stripped.length > 50 ? '...' : ''}"`);
            }
        }
    }
    /**
     * 处理消息
     */
    handleMessage(msg) {
        switch (msg.type) {
            case 'system':
                this.handleSystemMessage(msg);
                break;
            case 'assistant':
                this.clearApiRetryState();
                this.handleAssistantMessage(msg);
                break;
            case 'user':
                this.clearApiRetryState();
                this.handleUserMessage(msg);
                break;
            case 'result':
                this.handleResultMessage(msg);
                break;
            case 'error':
                this.clearApiRetryState();
                this.handleErrorMessage(msg);
                break;
            case 'stream_event':
                this.clearApiRetryState();
                this.handleStreamEvent(msg);
                break;
        }
    }
    /**
     * 清除 API 重试状态（收到正常消息时调用）
     */
    clearApiRetryState() {
        this.apiRetryEntryIndex = null;
        this.apiRetryEntryId = null;
    }
    /**
     * 处理系统消息
     */
    handleSystemMessage(msg) {
        if (msg.subtype === 'init' && msg.session_id) {
            this.msgStore.pushSessionId(msg.session_id);
            const patch = setSessionId(msg.session_id);
            this.msgStore.pushPatch(patch);
        }
        else if (msg.subtype === 'api_retry') {
            this.handleApiRetry(msg);
        }
    }
    /**
     * 处理 API 重试消息
     * 使用单条 entry 原地更新，避免每次重试刷屏
     */
    handleApiRetry(msg) {
        const attempt = msg.attempt ?? 0;
        const maxRetries = msg.max_retries ?? 10;
        const errorStatus = msg.error_status ?? 0;
        const errorType = msg.error ?? 'unknown_error';
        const content = `API error (${errorStatus} ${errorType}), retrying ${attempt}/${maxRetries}...`;
        const errorDetail = `HTTP ${errorStatus}: ${errorType}`;
        if (this.apiRetryEntryIndex != null && this.apiRetryEntryId != null) {
            // 更新已有的 entry
            const entry = createErrorMessage(content, errorDetail, this.apiRetryEntryId);
            const patch = replaceNormalizedEntry(this.apiRetryEntryIndex, entry);
            this.msgStore.pushPatch(patch);
        }
        else {
            // 创建新的 entry
            const entry = createErrorMessage(content, errorDetail);
            const index = this.indexProvider.next();
            const patch = addNormalizedEntry(index, entry);
            this.msgStore.pushPatch(patch);
            this.apiRetryEntryIndex = index;
            this.apiRetryEntryId = entry.id;
        }
    }
    /**
     * 处理助手消息
     * 通过 message.id 查找 stream_event 阶段创建的 entry，用完整内容 replace
     * 对于 tool_use（不走 stream_event），直接新建 entry
     */
    handleAssistantMessage(msg) {
        if (!msg.message?.content)
            return;
        // 检测 --include-partial-messages 产生的 partial assistant 消息。
        // partial 消息的 stop_reason 为 null，且 content 数组仅包含最新完成的 block，
        // 导致 contentIndex 与 stream_event 的 event.index 不匹配。
        // 对于 text/thinking，streaming 已实时展示，partial 消息无需重复处理。
        const isPartialMessage = msg.message.stop_reason === null;
        const messageId = msg.message.id || msg.message_id;
        const streamingState = messageId ? this.streamingMessages.get(messageId) : undefined;
        for (let contentIndex = 0; contentIndex < msg.message.content.length; contentIndex++) {
            const block = msg.message.content[contentIndex];
            if (block.type === 'tool_use' && block.name) {
                this.flushAssistantText();
                // partial assistant 消息的 content 数组会从 0 重新编号，不能再用 contentIndex
                // 去回查 stream_event 阶段的 block，否则会把已有的 thinking/text entry 误替换掉。
                // tool_use 应该用 block 级别 id（tool_use_id / id）做稳定关联；缺失时再回退到 messageId。
                const toolCallId = block.tool_use_id || block.id;
                const existingIndex = toolCallId ? this.toolEntryMap.get(toolCallId) : undefined;
                this.handleToolUse(block.name, block.input, toolCallId || messageId, existingIndex);
            }
            else if (block.type === 'text' && block.text) {
                if (isPartialMessage) {
                    continue;
                }
                // 优先用完整内容 replace stream_event 阶段的 entry；
                // 某些 provider 不返回 content_block_*，此时直接补建 assistant entry。
                const existingIndex = streamingState?.contents.get(contentIndex)?.entryIndex;
                if (existingIndex != null) {
                    const entry = createAssistantMessage(block.text);
                    const patch = replaceNormalizedEntry(existingIndex, entry);
                    this.msgStore.pushPatch(patch);
                }
                else if (!streamingState) {
                    const index = this.indexProvider.next();
                    const entry = createAssistantMessage(block.text);
                    const patch = addNormalizedEntry(index, entry);
                    this.msgStore.pushPatch(patch);
                }
            }
            else if (block.type === 'thinking' && block.thinking) {
                if (isPartialMessage) {
                    continue;
                }
                const existingIndex = streamingState?.contents.get(contentIndex)?.entryIndex;
                if (existingIndex != null) {
                    const entry = createThinking(block.thinking);
                    const patch = replaceNormalizedEntry(existingIndex, entry);
                    this.msgStore.pushPatch(patch);
                }
                else if (!streamingState) {
                    const index = this.indexProvider.next();
                    const entry = createThinking(block.thinking);
                    const patch = addNormalizedEntry(index, entry);
                    this.msgStore.pushPatch(patch);
                }
            }
        }
        // 不在此处删除 streamingMessages。
        // 由于启动参数包含 --include-partial-messages，同一个 message_id 可能产生多条
        // assistant 消息（每完成一个 content block 就输出一次 partial message）。
        // 保留 streaming state 让后续 partial 消息仍能找到已有 entry 进行 replace，
        // 避免因查找失败而重复创建 entry。
        // 清理时机：在 message_start 事件中，新消息开始时清理前一条消息的状态。
        // 提取 per-turn usage（每条 assistant 消息携带该轮的 usage）
        if (msg.message?.usage) {
            const u = msg.message.usage;
            if (typeof u.input_tokens === 'number') {
                this.lastTurnUsage = {
                    input_tokens: u.input_tokens || 0,
                    output_tokens: u.output_tokens || 0,
                    cache_creation_input_tokens: u.cache_creation_input_tokens || 0,
                    cache_read_input_tokens: u.cache_read_input_tokens || 0,
                };
            }
        }
    }
    /**
     * 追加助手文本
     */
    appendAssistantText(text) {
        if (this.currentAssistantIndex === null) {
            // 创建新的助手消息
            const entry = createAssistantMessage(text);
            const index = this.indexProvider.next();
            this.currentAssistantIndex = index;
            this.currentAssistantContent = text;
            const patch = addNormalizedEntry(index, entry);
            this.msgStore.pushPatch(patch);
        }
        else {
            // 追加到现有消息
            this.currentAssistantContent += text;
            const patch = updateEntryContent(this.currentAssistantIndex, this.currentAssistantContent);
            this.msgStore.pushPatch(patch);
        }
    }
    /**
     * 刷新助手文本
     */
    flushAssistantText() {
        this.currentAssistantIndex = null;
        this.currentAssistantContent = '';
    }
    /**
     * 处理工具使用
     */
    handleToolUse(toolName, input, toolCallId, existingIndex) {
        const action = toolNameToAction(toolName);
        const content = this.formatToolContent(toolName, input);
        // Extract todos from TodoWrite tool call
        const extras = this.extractTodoExtras(toolName, input);
        const entry = createToolUse(toolName, content, action, toolCallId, undefined, extras);
        if (existingIndex != null) {
            // replace stream_event 阶段创建的 entry
            const patch = replaceNormalizedEntry(existingIndex, entry);
            this.msgStore.pushPatch(patch);
            if (toolCallId) {
                this.toolEntryMap.set(toolCallId, existingIndex);
            }
        }
        else {
            const index = this.indexProvider.next();
            if (toolCallId) {
                this.toolEntryMap.set(toolCallId, index);
            }
            const patch = addNormalizedEntry(index, entry);
            this.msgStore.pushPatch(patch);
        }
    }
    /**
     * 格式化工具内容
     */
    formatToolContent(toolName, input) {
        if (!input || typeof input !== 'object') {
            return toolName;
        }
        const obj = input;
        switch (toolName) {
            case 'Read':
                return `Reading ${obj.file_path || obj.path || 'file'}`;
            case 'Edit':
                return `Editing ${obj.file_path || obj.path || 'file'}`;
            case 'Write':
                return `Writing ${obj.file_path || obj.path || 'file'}`;
            case 'Bash':
                return `Running: ${obj.command || 'command'}`;
            case 'Grep':
                return `Searching for: ${obj.pattern || 'pattern'}`;
            case 'Glob':
                return `Finding files: ${obj.pattern || 'pattern'}`;
            case 'WebFetch':
                return `Fetching: ${obj.url || 'URL'}`;
            case 'Task':
                return `Creating task: ${obj.description || 'task'}`;
            case 'TodoWrite':
                return 'Updating todo list';
            default:
                return `${toolName}: ${JSON.stringify(input).slice(0, 100)}`;
        }
    }
    /**
     * 从 TodoWrite 工具调用中提取 todo 数据
     */
    extractTodoExtras(toolName, input) {
        if (toolName !== 'TodoWrite' || !input || typeof input !== 'object')
            return undefined;
        const obj = input;
        const todos = obj.todos;
        if (!Array.isArray(todos))
            return undefined;
        return {
            todos: todos.map((t) => ({
                content: String(t.content || ''),
                status: String(t.status || 'pending'),
                priority: t.priority ? String(t.priority) : null,
            })),
            todoOperation: 'write',
        };
    }
    /**
     * 添加思考内容
     */
    addThinking(text) {
        const entry = createThinking(text);
        const index = this.indexProvider.next();
        const patch = addNormalizedEntry(index, entry);
        this.msgStore.pushPatch(patch);
    }
    /**
     * 处理用户消息
     * 跳过 — 用户消息已由 session.service.ts 在收到请求时主动推送，
     * 这里如果再处理 Claude Code 回显的 user message 会导致重复
     */
    handleUserMessage(_msg) {
        // noop: 避免与 session.service.ts 的 createUserMessage 重复
    }
    /**
     * 处理结果消息
     */
    handleResultMessage(msg) {
        if (msg.subtype === 'tool_result' && msg.tool_use_id) {
            const index = this.toolEntryMap.get(msg.tool_use_id);
            if (index !== undefined) {
                const status = msg.tool_result?.is_error ? 'failed' : 'success';
                const patch = updateToolStatus(index, status);
                this.msgStore.pushPatch(patch);
            }
        }
        // 最终错误结果（如 API 重试全部失败后）
        if (msg.is_error && msg.result) {
            const content = msg.result;
            // 如果有 api_retry entry，替换它为最终错误；否则新建
            if (this.apiRetryEntryIndex != null && this.apiRetryEntryId != null) {
                const entry = createErrorMessage(content, content, this.apiRetryEntryId);
                const patch = replaceNormalizedEntry(this.apiRetryEntryIndex, entry);
                this.msgStore.pushPatch(patch);
            }
            else {
                const entry = createErrorMessage(content, content);
                const index = this.indexProvider.next();
                const patch = addNormalizedEntry(index, entry);
                this.msgStore.pushPatch(patch);
            }
            this.clearApiRetryState();
        }
        // success result 中如果有 result 文本且之前没有 assistant 消息输出，显示该文本
        // 例如 "Unknown skill: testsdfs" 这类 Claude Code 直接返回的结果
        if (msg.subtype === 'success' && msg.result && !msg.is_error) {
            // 检查是否有过 assistant 消息（num_turns <= 0 或 duration_api_ms === 0 表示没有实际 API 调用）
            if (msg.duration_api_ms === 0 || msg.num_turns === 0) {
                const entry = createAssistantMessage(msg.result);
                const index = this.indexProvider.next();
                const patch = addNormalizedEntry(index, entry);
                this.msgStore.pushPatch(patch);
            }
        }
        // 从 success result 中提取 token 用量
        if (msg.subtype === 'success') {
            try {
                // 提取 context window 大小
                let modelContextWindow;
                if (msg.modelUsage) {
                    const firstModel = Object.values(msg.modelUsage)[0];
                    if (firstModel?.contextWindow) {
                        modelContextWindow = firstModel.contextWindow;
                    }
                }
                if (!modelContextWindow && msg.model_usage) {
                    const firstModel = Object.values(msg.model_usage)[0];
                    if (firstModel?.context_window) {
                        modelContextWindow = firstModel.context_window;
                    }
                }
                // 计算当前上下文窗口占用量
                // 优先使用 lastTurnUsage（最后一条 assistant 消息的 per-turn usage），
                // 这与 Claude Code 自身 statusline 的计算方式一致：
                // totalTokens = input_tokens + cache_creation_input_tokens + cache_read_input_tokens
                let totalTokens;
                if (this.lastTurnUsage) {
                    const u = this.lastTurnUsage;
                    totalTokens = u.input_tokens + u.cache_creation_input_tokens + u.cache_read_input_tokens;
                }
                else if (msg.usage) {
                    // 回退：没有 assistant 消息时用 result.usage（累计值，不太准确）
                    const usage = msg.usage;
                    totalTokens =
                        (usage.input_tokens || 0) +
                            (usage.output_tokens || 0) +
                            (usage.cache_creation_input_tokens || 0) +
                            (usage.cache_read_input_tokens || 0);
                }
                if (totalTokens != null) {
                    const entry = createTokenUsageInfo(totalTokens, modelContextWindow);
                    const index = this.indexProvider.next();
                    const patch = addNormalizedEntry(index, entry);
                    this.msgStore.pushPatch(patch);
                }
            }
            catch {
                // token 提取失败不影响正常解析
            }
        }
    }
    /**
     * 处理错误消息
     */
    handleErrorMessage(msg) {
        const entry = createErrorMessage(msg.error || 'Unknown error', msg.error);
        const index = this.indexProvider.next();
        const patch = addNormalizedEntry(index, entry);
        this.msgStore.pushPatch(patch);
    }
    /**
     * 处理 stream_event（增量流式消息）
     */
    handleStreamEvent(msg) {
        const event = msg.event;
        if (!event)
            return;
        switch (event.type) {
            case 'message_start': {
                this.streamingRole = event.message?.role || null;
                const messageId = event.message?.id;
                if (messageId && this.streamingRole === 'assistant') {
                    this.streamingMessageId = messageId;
                    // 清理前一条消息的 streaming state，新消息开始意味着前一条已完成
                    this.streamingMessages.clear();
                    this.streamingMessages.set(messageId, { role: 'assistant', contents: new Map() });
                }
                else {
                    this.streamingMessageId = null;
                }
                break;
            }
            case 'content_block_start': {
                const index = event.index;
                const block = event.content_block;
                if (index == null || !block)
                    break;
                const kind = block.type === 'thinking' ? 'thinking' : 'text';
                const initial = kind === 'thinking' ? (block.thinking || '') : (block.text || '');
                // Create the entry immediately
                let entryIndex;
                if (kind === 'thinking') {
                    this.flushAssistantText();
                    const entry = createThinking(initial);
                    entryIndex = this.indexProvider.next();
                    const patch = addNormalizedEntry(entryIndex, entry);
                    this.msgStore.pushPatch(patch);
                }
                else {
                    // text block — start or reuse assistant message
                    if (this.currentAssistantIndex === null) {
                        const entry = createAssistantMessage(initial);
                        entryIndex = this.indexProvider.next();
                        this.currentAssistantIndex = entryIndex;
                        this.currentAssistantContent = initial;
                        const patch = addNormalizedEntry(entryIndex, entry);
                        this.msgStore.pushPatch(patch);
                    }
                    else {
                        entryIndex = this.currentAssistantIndex;
                        if (initial) {
                            this.currentAssistantContent += initial;
                            const patch = updateEntryContent(entryIndex, this.currentAssistantContent);
                            this.msgStore.pushPatch(patch);
                        }
                    }
                }
                const streamBlock = { kind, entryIndex, buffer: initial };
                this.streamingBlocks.set(index, streamBlock);
                // 记录到 StreamingMessageState
                if (this.streamingMessageId) {
                    const state = this.streamingMessages.get(this.streamingMessageId);
                    if (state) {
                        state.contents.set(index, streamBlock);
                    }
                }
                break;
            }
            case 'content_block_delta': {
                const index = event.index;
                const delta = event.delta;
                if (index == null || !delta)
                    break;
                const block = this.streamingBlocks.get(index);
                if (!block)
                    break;
                const chunk = delta.type === 'thinking_delta' ? (delta.thinking || '') : (delta.text || '');
                if (!chunk)
                    break;
                block.buffer += chunk;
                if (block.kind === 'thinking') {
                    const patch = updateEntryContent(block.entryIndex, block.buffer);
                    this.msgStore.pushPatch(patch);
                }
                else {
                    this.currentAssistantContent = block.buffer;
                    const patch = updateEntryContent(block.entryIndex, this.currentAssistantContent);
                    this.msgStore.pushPatch(patch);
                }
                break;
            }
            case 'content_block_stop': {
                const index = event.index;
                if (index == null)
                    break;
                const block = this.streamingBlocks.get(index);
                if (!block)
                    break;
                if (block.kind === 'text') {
                    // Keep currentAssistantIndex so next text block appends,
                    // but flush will be called by next thinking block or tool_use
                }
                this.streamingBlocks.delete(index);
                break;
            }
            case 'message_stop': {
                this.flushAssistantText();
                this.streamingBlocks.clear();
                this.streamingRole = null;
                // 不清理 streamingMessages — 保留到 handleAssistantMessage 消费
                this.streamingMessageId = null;
                break;
            }
        }
    }
    /**
     * 完成解析
     * exitCode 非 0 且没有解析到有效 JSON 消息时，将非 JSON 输出作为错误推送
     */
    finish(exitCode) {
        // 处理剩余的缓冲区
        if (this.buffer.trim()) {
            this.parseLine(this.buffer);
        }
        this.flushAssistantText();
        // 进程异常退出时，将收集到的非 JSON 输出作为错误信息推送
        const isFailed = typeof exitCode === 'number' && exitCode !== 0;
        if (isFailed && this.nonJsonLines.length > 0) {
            const errorText = this.nonJsonLines.join('\n').trim();
            if (errorText) {
                const entry = createErrorMessage(`Agent process exited with code ${exitCode}:\n${errorText}`, errorText);
                const index = this.indexProvider.next();
                const patch = addNormalizedEntry(index, entry);
                this.msgStore.pushPatch(patch);
            }
        }
    }
}
/**
 * 创建解析器并连接到 PTY
 */
export function createClaudeCodeParser(msgStore) {
    return new ClaudeCodeParser(msgStore);
}
//# sourceMappingURL=claude-code-parser.js.map