import { contentArrayHasAssistantThinkingBlock } from "./thinking-blocks.js";
function getMessageContent(message) {
    return "content" in message && Array.isArray(message.content) ? message.content : undefined;
}
function getToolCallContentBlockId(block) {
    if (!block || typeof block !== "object")
        return undefined;
    const candidate = block;
    return candidate.type === "toolCall" && typeof candidate.id === "string" ? candidate.id : undefined;
}
function getToolResultCallId(message) {
    if (message.role !== "toolResult")
        return undefined;
    const toolCallId = message.toolCallId;
    return typeof toolCallId === "string" ? toolCallId : undefined;
}
function collectToolReferences(path) {
    const callsById = new Map();
    const resultsByCallId = new Map();
    for (const entry of path) {
        if (entry.type !== "message")
            continue;
        if (entry.message.role === "assistant") {
            const content = getMessageContent(entry.message);
            if (!content)
                continue;
            const hasThinkingContent = contentArrayHasAssistantThinkingBlock(entry.message.content);
            for (const [blockIndex, block] of content.entries()) {
                const callId = getToolCallContentBlockId(block);
                if (!callId)
                    continue;
                const refs = callsById.get(callId) ?? [];
                refs.push({ entry, blockIndex, hasThinkingContent });
                callsById.set(callId, refs);
            }
            continue;
        }
        const resultCallId = getToolResultCallId(entry.message);
        if (!resultCallId)
            continue;
        const refs = resultsByCallId.get(resultCallId) ?? [];
        refs.push({ entry });
        resultsByCallId.set(resultCallId, refs);
    }
    return { callsById, resultsByCallId };
}
function isMessageEntryEffectivelyDeleted(entry, filters) {
    if (filters.deletedEntryIds.has(entry.id))
        return true;
    const deletedBlocks = filters.deletedContentBlocks.get(entry.id);
    if (!deletedBlocks || deletedBlocks.size === 0)
        return false;
    const content = getMessageContent(entry.message);
    return content !== undefined && content.length > 0 && content.every((_block, index) => deletedBlocks.has(index));
}
function isToolCallDeleted(ref, filters) {
    if (isMessageEntryEffectivelyDeleted(ref.entry, filters))
        return true;
    return filters.deletedContentBlocks.get(ref.entry.id)?.has(ref.blockIndex) === true;
}
function isToolResultDeleted(ref, filters) {
    return isMessageEntryEffectivelyDeleted(ref.entry, filters);
}
function addEntryDeletion(filters, entryId) {
    if (filters.deletedEntryIds.has(entryId))
        return false;
    filters.deletedEntryIds.add(entryId);
    filters.deletedContentBlocks.delete(entryId);
    return true;
}
function addToolCallDeletion(filters, ref) {
    if (filters.deletedEntryIds.has(ref.entry.id))
        return false;
    const deletedBlocks = filters.deletedContentBlocks.get(ref.entry.id) ?? new Set();
    if (deletedBlocks.has(ref.blockIndex))
        return false;
    deletedBlocks.add(ref.blockIndex);
    filters.deletedContentBlocks.set(ref.entry.id, deletedBlocks);
    return true;
}
function restoreResultEntry(filters, entryId) {
    const hadEntryDeletion = filters.deletedEntryIds.delete(entryId);
    const hadBlockDeletion = filters.deletedContentBlocks.delete(entryId);
    return hadEntryDeletion || hadBlockDeletion;
}
/**
 * Reconcile persisted context-compaction filters in place so replay never
 * retains only one side of a tool-call/tool-result pair. Callers should pass a
 * fresh, unshared filter set; this mutates and returns that same object. The
 * fixpoint normally converges in one or two passes, while the bounded pass
 * count is only a non-termination backstop for malformed historical sessions.
 */
export function reconcilePersistedToolDependencyFilters(path, filters) {
    const { callsById, resultsByCallId } = collectToolReferences(path);
    if (callsById.size === 0 || resultsByCallId.size === 0)
        return filters;
    let changed = true;
    let remainingPasses = Math.max(1, path.length * 2);
    while (changed && remainingPasses > 0) {
        changed = false;
        remainingPasses -= 1;
        for (const [callId, callRefs] of callsById) {
            const resultRefs = resultsByCallId.get(callId) ?? [];
            if (resultRefs.length === 0)
                continue;
            const callDeleted = callRefs.every((ref) => isToolCallDeleted(ref, filters));
            if (callDeleted) {
                for (const resultRef of resultRefs) {
                    if (!isToolResultDeleted(resultRef, filters)) {
                        changed = addEntryDeletion(filters, resultRef.entry.id) || changed;
                    }
                }
                continue;
            }
            for (const resultRef of resultRefs) {
                if (!isToolResultDeleted(resultRef, filters))
                    continue;
                const retainedThinkingCall = callRefs.some((ref) => ref.hasThinkingContent && !isToolCallDeleted(ref, filters));
                if (retainedThinkingCall) {
                    changed = restoreResultEntry(filters, resultRef.entry.id) || changed;
                    continue;
                }
                for (const callRef of callRefs) {
                    if (!isToolCallDeleted(callRef, filters)) {
                        changed = addToolCallDeletion(filters, callRef) || changed;
                    }
                }
            }
        }
    }
    return filters;
}
//# sourceMappingURL=session-manager-tool-dependencies.js.map