import { saveToolDetails } from '../services/tool-detail.js';
export function createAgentMessagePartsTracker(input) {
    const output = [];
    const reasoning = [];
    const toolDetails = new Map();
    const toolUseDetailIds = new Map();
    return {
        output,
        reasoning,
        toolDetails,
        handleEvent(event) {
            if (event.type === 'reasoning') {
                reasoning.push({ text: event.text, status: event.status });
                input.onOutput?.(event.text);
                return;
            }
            if (event.type === 'tool_use') {
                const detailId = buildToolDetailId(event.id, event.line);
                toolUseDetailIds.set(event.id, detailId);
                toolDetails.set(detailId, {
                    id: detailId,
                    workspaceId: input.workspaceId,
                    channelId: input.channelId,
                    messageId: input.messageId,
                    title: summarizeToolLine(event.line, input.workspaceRoot).title,
                    raw: event.line,
                    input: event.input,
                    createdAt: new Date().toISOString(),
                });
                saveToolDetails(input.workspaceId, input.channelId, Array.from(toolDetails.values()));
                output.push(event.line);
                input.onOutput?.(event.line);
                return;
            }
            if (event.type === 'tool_result') {
                const detail = findToolDetailForResult(event.toolUseId, event.result, toolUseDetailIds, toolDetails);
                if (detail) {
                    detail.output = event.result;
                    detail.updatedAt = new Date().toISOString();
                    saveToolDetails(input.workspaceId, input.channelId, [detail]);
                }
                return;
            }
            if (event.type === 'session' || event.type === 'hook_event')
                return;
            output.push(event.line);
            input.onOutput?.(event.line);
        },
        buildParts({ sessionId, workspaceRoot, model, usage, persistentContext, success, error }) {
            return buildAgentMessageParts({
                sessionId,
                workspaceRoot,
                model,
                usage,
                output,
                reasoning,
                toolDetails,
                persistentContext,
                success,
                error,
            });
        },
    };
}
function buildAgentMessageParts(input) {
    const extractedReasoning = extractInlineReasoning(normalizeOutputLines(input.output));
    const lines = extractedReasoning.lines;
    const finalTextRange = findFinalTextRange(lines);
    const finalText = finalTextRange
        ? collapseRepeatedTextBlock(lines.slice(finalTextRange.start, finalTextRange.end + 1)).join('\n').trim()
        : '';
    const usage = input.usage ?? extractUsage(lines);
    const parts = [];
    const chainItems = buildChainItems(lines, finalTextRange, finalText, input.workspaceRoot, input.toolDetails, input.persistentContext);
    const reasoningText = normalizeReasoningText([
        ...(input.reasoning ?? []),
        ...extractedReasoning.reasoning.map((text) => ({ text })),
    ]);
    if (reasoningText) {
        parts.push({
            id: `reasoning-${input.sessionId}`,
            type: 'reasoning',
            text: reasoningText,
            status: input.success ? 'completed' : 'streaming',
        });
    }
    if (chainItems.length > 0) {
        parts.push({
            id: `chain-${input.sessionId}`,
            type: 'chain',
            chains: chainItems,
        });
    }
    for (const subagent of extractSubagentBlocks(lines, input.sessionId, input.toolDetails)) {
        parts.push(subagent);
    }
    if (hasTokenUsage(usage)) {
        parts.push({
            id: `context-${input.sessionId}`,
            type: 'context',
            usedTokens: getTotalTokens(usage),
            maxTokens: 128_000,
            modelId: input.model,
            usage,
        });
    }
    if (input.error) {
        parts.push({
            id: `terminal-error-${input.sessionId}`,
            type: 'terminal',
            output: input.error,
            status: 'error',
        });
    }
    if (finalText && finalText !== input.error) {
        parts.push({
            id: `text-${input.sessionId}`,
            type: 'text',
            text: finalText,
        });
    }
    return parts;
}
function normalizeReasoningText(reasoning) {
    if (!reasoning?.length)
        return '';
    return collapseRepeatedTextBlock(reasoning
        .map((item) => item.text.trim())
        .filter(Boolean)).join('\n\n').trim();
}
function extractInlineReasoning(lines) {
    const reasoning = [];
    const outputLines = [];
    let buffer = [];
    let inThinkBlock = false;
    const flushReasoning = () => {
        const text = buffer.join('\n').trim();
        if (text)
            reasoning.push(text);
        buffer = [];
    };
    for (const line of lines) {
        let rest = line;
        let visible = '';
        while (rest) {
            if (inThinkBlock) {
                const closeIndex = rest.search(/<\/think>/i);
                if (closeIndex < 0) {
                    buffer.push(rest);
                    rest = '';
                    continue;
                }
                buffer.push(rest.slice(0, closeIndex));
                flushReasoning();
                rest = rest.slice(closeIndex).replace(/^<\/think>/i, '');
                inThinkBlock = false;
                continue;
            }
            const openIndex = rest.search(/<think>/i);
            if (openIndex < 0) {
                visible += rest;
                rest = '';
                continue;
            }
            visible += rest.slice(0, openIndex);
            rest = rest.slice(openIndex).replace(/^<think>/i, '');
            inThinkBlock = true;
        }
        const cleaned = visible.trimEnd();
        if (cleaned.trim())
            outputLines.push(cleaned);
    }
    if (inThinkBlock)
        flushReasoning();
    return { lines: outputLines, reasoning };
}
function hasTokenUsage(usage) {
    return Boolean(usage.totalTokens || usage.inputTokens || usage.outputTokens || usage.cachedInputTokens || usage.reasoningTokens);
}
function getTotalTokens(usage) {
    return usage.totalTokens ?? (usage.inputTokens ?? 0) + (usage.outputTokens ?? 0) + (usage.cachedInputTokens ?? 0) + (usage.reasoningTokens ?? 0);
}
function normalizeOutputLines(output) {
    const lines = output
        .flatMap((line) => line.split(/\r?\n/))
        .map((line) => line.trimEnd())
        .filter((line) => line.trim());
    const seenInitLines = new Set();
    return lines.filter((line) => {
        if (isIgnorableToolProgressLine(line))
            return false;
        if (!/^Claude Code initialized\b/i.test(line))
            return true;
        if (seenInitLines.has(line))
            return false;
        seenInitLines.add(line);
        return true;
    });
}
function collapseRepeatedTextBlock(lines) {
    let next = [...lines];
    while (next.length > 1 && next.length % 2 === 0) {
        const middle = next.length / 2;
        const first = next.slice(0, middle);
        const second = next.slice(middle);
        if (!sameTextBlock(first, second))
            break;
        next = first;
    }
    return next;
}
function sameTextBlock(left, right) {
    if (left.length !== right.length)
        return false;
    return normalizeMessageText(left.join('\n')) === normalizeMessageText(right.join('\n'));
}
function findFinalTextRange(lines) {
    let end = -1;
    for (let index = lines.length - 1; index >= 0; index -= 1) {
        if (isFinalAnswerLine(lines[index])) {
            end = index;
            break;
        }
    }
    if (end < 0)
        return null;
    let start = end;
    for (let index = end - 1; index >= 0; index -= 1) {
        if (!isFinalAnswerLine(lines[index]))
            break;
        start = index;
    }
    return { start, end };
}
function buildChainItems(lines, finalTextRange, finalText, workspaceRoot, toolDetails, persistentContext) {
    let toolIndex = 0;
    let messageIndex = 0;
    const items = [];
    const toolDetailMatchCounts = new Map();
    let messageBuffer = [];
    const persistentContextItem = buildPersistentContextToolItem(persistentContext);
    if (persistentContextItem) {
        items.push(persistentContextItem);
        toolIndex += 1;
    }
    const flushMessageBuffer = () => {
        if (messageBuffer.length === 0)
            return;
        const text = messageBuffer.join('\n').trim();
        if (text) {
            items.push({
                id: `message-${messageIndex}`,
                title: summarizeMessageTitle(text),
                text,
                kind: 'message',
                status: 'completed',
            });
            messageIndex += 1;
        }
        messageBuffer = [];
    };
    for (let index = 0; index < lines.length; index += 1) {
        if (finalTextRange && index >= finalTextRange.start && index <= finalTextRange.end)
            continue;
        const line = lines[index];
        if (finalText && isSameMessageText(line, finalText))
            continue;
        if (isSubagentToolLine(line))
            continue;
        if (isToolLikeLine(line)) {
            flushMessageBuffer();
            items.push(buildToolTodo(line, toolIndex, workspaceRoot, toolDetails, toolDetailMatchCounts));
            toolIndex += 1;
            continue;
        }
        if (isFinalAnswerLine(line)) {
            messageBuffer.push(line);
        }
    }
    flushMessageBuffer();
    return items;
}
function buildToolTodo(line, index, workspaceRoot, toolDetails, toolDetailMatchCounts) {
    const summary = summarizeToolLine(line, workspaceRoot);
    const detailId = findToolDetailId(line, toolDetails, toolDetailMatchCounts);
    return {
        id: `tool-${index}`,
        kind: 'tool',
        title: summary.title,
        description: summary.description,
        status: 'completed',
        toolName: summary.toolName,
        filePath: summary.filePath,
        command: summary.command,
        detailId,
    };
}
function buildPersistentContextToolItem(persistentContext) {
    const counts = persistentContext?.counts;
    if (!counts?.total)
        return null;
    const description = [
        counts.claudeMd ? `${counts.claudeMd} claude.md` : null,
        counts.agentsMd ? `${counts.agentsMd} AGENTS.md` : null,
    ].filter(Boolean).join(', ') || `${counts.total} instruction files`;
    return {
        id: 'tool-persistent-context',
        kind: 'tool',
        title: 'Load claude.md',
        description,
        status: 'completed',
        toolName: 'LoadClaudeMd',
    };
}
function summarizeToolLine(line, workspaceRoot) {
    const trimmed = line.trim();
    const toolName = extractToolName(trimmed);
    const inputDescription = extractQuotedField(trimmed, 'description');
    const filePath = toWorkspaceRelativePath(extractQuotedField(trimmed, 'file_path') ?? extractQuotedField(trimmed, 'path'), workspaceRoot);
    const command = extractQuotedField(trimmed, 'command') ?? extractCommand(trimmed, toolName);
    const baseName = filePath?.split(/[\\/]/).filter(Boolean).at(-1);
    if (toolName) {
        if (filePath) {
            return {
                title: `${humanizeToolName(toolName)} ${baseName ?? filePath}`,
                description: inputDescription ?? filePath,
                toolName,
                filePath,
            };
        }
        if (command) {
            return {
                title: `${humanizeToolName(toolName)} command`,
                description: inputDescription,
                toolName,
                command,
            };
        }
        const searchSummary = extractSearchParams(trimmed, toolName, workspaceRoot);
        if (searchSummary)
            return { ...searchSummary, toolName };
        const todoCount = extractTodoCount(trimmed);
        if (todoCount !== undefined) {
            return {
                title: `Update ${todoCount} ${todoCount === 1 ? 'todo' : 'todos'}`,
                toolName,
            };
        }
        return {
            title: humanizeToolName(toolName),
            toolName,
        };
    }
    return { title: trimmed };
}
function extractSearchParams(line, toolName, workspaceRoot) {
    if (!/^(Glob|Grep|Search|SemanticSearch|WebSearch|WebFetch|Fetch)$/i.test(toolName))
        return null;
    const pattern = extractQuotedField(line, 'pattern') ?? extractQuotedField(line, 'query');
    const glob = extractQuotedField(line, 'glob');
    const searchPath = toWorkspaceRelativePath(extractQuotedField(line, 'path'), workspaceRoot);
    const url = extractQuotedField(line, 'url');
    const label = humanizeToolName(toolName);
    if (/^(WebSearch|WebFetch|Fetch)$/i.test(toolName)) {
        if (url)
            return { title: `${label} ${truncate(url, 60)}` };
        if (pattern)
            return { title: `${label} ${truncate(pattern, 60)}` };
        return { title: label };
    }
    if (/^Glob$/i.test(toolName)) {
        const parts = [];
        if (pattern)
            parts.push(truncate(pattern, 50));
        else if (glob)
            parts.push(truncate(glob, 50));
        const title = parts.length ? `${label} ${parts.join(' ')}` : label;
        const descParts = [];
        if (pattern && glob)
            descParts.push(`glob: ${glob}`);
        if (searchPath)
            descParts.push(`in ${searchPath}`);
        return { title, description: descParts.length ? descParts.join(', ') : undefined };
    }
    const parts = [];
    if (pattern)
        parts.push(truncate(pattern, 40));
    const title = parts.length ? `${label} ${parts.join(' ')}` : label;
    const descParts = [];
    if (glob)
        descParts.push(`glob: ${glob}`);
    if (searchPath)
        descParts.push(`in ${searchPath}`);
    return { title, description: descParts.length ? descParts.join(', ') : undefined };
}
function findToolDetailForResult(toolUseId, result, toolUseDetailIds, toolDetails) {
    const detailId = toolUseId ? toolUseDetailIds.get(toolUseId) : undefined;
    if (detailId)
        return toolDetails.get(detailId);
    if (isSubagentToolResult(result)) {
        const detail = Array.from(toolDetails.values()).reverse().find((candidate) => {
            return candidate.output === undefined && /^Tool:\s*Task\b/i.test(candidate.raw);
        });
        if (detail)
            return detail;
    }
    return Array.from(toolDetails.values()).reverse().find((detail) => detail.output === undefined);
}
function isSubagentToolResult(result) {
    if (!result || typeof result !== 'object' || Array.isArray(result))
        return false;
    const record = result;
    return (typeof record.agentId === 'string'
        || typeof record.agentType === 'string'
        || (record.status === 'completed' && Array.isArray(record.content)));
}
function extractSubagentBlocks(lines, sessionId, toolDetails) {
    const matchCounts = new Map();
    return lines
        .map((line, index) => {
        const match = line.match(/^Tool:\s*Task\b\s*(.*)$/i);
        if (!match)
            return null;
        const name = line.match(/\bdescription=(["'])(.*?)\1/i)?.[2] || `Subagent ${index + 1}`;
        const prompt = line.match(/\bprompt=(["'])(.*?)\1/i)?.[2];
        const detailId = findToolDetailId(line, toolDetails, matchCounts);
        const detail = detailId ? toolDetails?.get(detailId) : undefined;
        const output = stringifySubagentOutput(detail?.output);
        return {
            id: `subagent-${sessionId}-${index}`,
            type: 'subagent',
            name,
            instructions: prompt,
            output,
        };
    })
        .filter((part) => Boolean(part));
}
function stringifySubagentOutput(output) {
    if (output === undefined || output === null)
        return undefined;
    if (typeof output === 'string')
        return output.trim() || undefined;
    if (Array.isArray(output)) {
        const text = output.flatMap((item) => {
            if (typeof item === 'string')
                return [item];
            if (!item || typeof item !== 'object')
                return [];
            const record = item;
            return typeof record.text === 'string' ? [record.text] : [];
        }).join('\n').trim();
        return text || undefined;
    }
    if (typeof output === 'object') {
        const record = output;
        if (typeof record.result === 'string' && record.result.trim())
            return record.result.trim();
        if (typeof record.summary === 'string' && record.summary.trim())
            return record.summary.trim();
        if (Array.isArray(record.content))
            return stringifySubagentOutput(record.content);
    }
    return undefined;
}
function findToolDetailId(line, toolDetails, matchCounts) {
    if (!toolDetails)
        return undefined;
    const targetMatchIndex = matchCounts?.get(line) ?? 0;
    let matchIndex = 0;
    for (const [id, detail] of toolDetails) {
        if (detail.raw !== line)
            continue;
        if (matchIndex === targetMatchIndex) {
            matchCounts?.set(line, targetMatchIndex + 1);
            return id;
        }
        matchIndex += 1;
    }
    return undefined;
}
function buildToolDetailId(toolUseId, line) {
    const safeToolUseId = toolUseId.replace(/[^a-zA-Z0-9_-]/g, '-');
    if (safeToolUseId)
        return `tool-${safeToolUseId}`;
    return `tool-${hashString(line)}`;
}
function hashString(value) {
    let hash = 0;
    for (let index = 0; index < value.length; index += 1) {
        hash = ((hash << 5) - hash + value.charCodeAt(index)) | 0;
    }
    return Math.abs(hash).toString(36);
}
function isSameMessageText(left, right) {
    return normalizeMessageText(left) === normalizeMessageText(right);
}
function normalizeMessageText(text) {
    return text.trim().replace(/\s+/g, ' ');
}
function summarizeMessageTitle(text) {
    const normalized = text.trim().replace(/\s+/g, ' ');
    if (!normalized)
        return 'AI message';
    return normalized.length > 80 ? `${normalized.slice(0, 77)}...` : normalized;
}
function isToolLikeLine(line) {
    return /^(Using|Tool:|Read|Write|Edit|MultiEdit|Bash|Search|Grep|Glob|Todo|Task|Web|Fetch|Claude Code initialized|Codex initialized|.+ running \(\d+s\))/i.test(line.trim());
}
function isIgnorableToolProgressLine(line) {
    const raw = line.trim();
    const trimmed = stripSubagentProgressPrefix(raw);
    const isSubagentProgress = trimmed !== raw;
    return /^Claude$/i.test(trimmed)
        || /^(Reading|Searching)(\.{1,3}|…)?$/i.test(trimmed)
        || (isSubagentProgress && /^(Reading|Searching)\s+\S.+$/i.test(trimmed))
        || /^(Read|Search|Grep|Glob|SemanticSearch|WebSearch)\s+running\s+\(\d+s\)$/i.test(trimmed);
}
function stripSubagentProgressPrefix(line) {
    return line.replace(/^\[[^\]]+\]\s+subagent progress\s+\|\s*/i, '');
}
function isFinalAnswerLine(line) {
    return !isToolLikeLine(line) && !/^(\[.*\]|Agent runtime configuration:|Conversation history:)/.test(line.trim());
}
function isSubagentToolLine(line) {
    return /^Tool:\s*Task\b/i.test(line.trim());
}
function extractToolName(line) {
    return line.match(/^Tool:\s*([A-Za-z][\w-]*)\b/)?.[1]
        ?? line.match(/^([A-Za-z][\w-]*)\s+running\s+\(\d+s\)/)?.[1]
        ?? line.match(/^([A-Za-z][\w-]*):?\s+/)?.[1];
}
function humanizeToolName(toolName) {
    const labels = {
        Read: 'Read',
        Write: 'Write',
        Edit: 'Edit',
        MultiEdit: 'Edit',
        Bash: 'Run',
        TodoWrite: 'Update todos',
        Grep: 'Search',
        Glob: 'Find files',
        Search: 'Search',
        SemanticSearch: 'Semantic search',
        WebSearch: 'Web search',
        WebFetch: 'Fetch',
        Fetch: 'Fetch',
        Task: 'Run subagent',
    };
    return labels[toolName] ?? toolName.replace(/([a-z])([A-Z])/g, '$1 $2');
}
function extractQuotedField(line, key) {
    const quoted = line.match(new RegExp(`\\b${key}=(["'])(.*?)\\1`))?.[2];
    if (quoted)
        return quoted;
    const json = line.match(new RegExp(`"${key}"\\s*:\\s*"([^"]+)"`))?.[1];
    return json;
}
function extractCommand(line, toolName) {
    if (!toolName || !/^(Bash|Shell|Command)$/i.test(toolName))
        return undefined;
    const command = line.replace(/^Tool:\s*/i, '').replace(new RegExp(`^${toolName}:?\\s*`, 'i'), '').trim();
    return command || undefined;
}
function extractTodoCount(line) {
    const matches = line.match(/"content"\s*:/g);
    return matches?.length;
}
function toWorkspaceRelativePath(path, workspaceRoot) {
    if (!path)
        return undefined;
    if (!workspaceRoot || !path.startsWith(workspaceRoot))
        return path;
    return path.slice(workspaceRoot.length).replace(/^[/\\]/, '');
}
function truncate(str, max) {
    return str.length > max ? `${str.slice(0, max - 3)}...` : str;
}
function extractUsage(lines) {
    const usage = {};
    for (const line of lines) {
        const total = line.match(/\btotal(?: tokens)?:\s*([\d,]+)/i)?.[1];
        const input = line.match(/\binput(?: tokens)?:\s*([\d,]+)/i)?.[1];
        const output = line.match(/\boutput(?: tokens)?:\s*([\d,]+)/i)?.[1];
        const cached = line.match(/\bcached(?: input)?(?: tokens)?:\s*([\d,]+)/i)?.[1];
        const reasoning = line.match(/\breasoning(?: tokens)?:\s*([\d,]+)/i)?.[1];
        if (total)
            usage.totalTokens = parseTokenNumber(total);
        if (input)
            usage.inputTokens = parseTokenNumber(input);
        if (output)
            usage.outputTokens = parseTokenNumber(output);
        if (cached)
            usage.cachedInputTokens = parseTokenNumber(cached);
        if (reasoning)
            usage.reasoningTokens = parseTokenNumber(reasoning);
    }
    return usage;
}
function parseTokenNumber(value) {
    return Number(value.replace(/,/g, ''));
}
//# sourceMappingURL=agent-message-parts.js.map