import { buildTranscriptEventIndex } from './transcript-events/index.js';
import { compactConversation } from './conversation-compaction.js';
import { cloneBranchMap, cloneMessages, deriveConversationTitle, messagesToInternal, restoreBranchMap, } from './conversation-utils.js';
import { applyDiffContent, parseDiffForApply } from './conversation-diff.js';
export class ConversationManager {
    messages = [];
    _title = '';
    _titleSource = 'system';
    sessionMemoryStore = null;
    sessionLineageTracker = {
        addCompactionEntry: () => { },
    };
    branches = new Map();
    currentBranch = 'main';
    streamingMessageIndex = -1;
    undoStack = [];
    _messagesRevision = 0;
    _cachedLLMMessages = null;
    _cachedLLMRevision = -1;
    constructor() { }
    findToolName(callId) {
        for (let i = this.messages.length - 1; i >= 0; i--) {
            const message = this.messages[i];
            if (message.role !== 'assistant' || !message.toolCalls?.length)
                continue;
            const match = message.toolCalls.find((call) => call.id === callId);
            if (match?.name)
                return match.name;
        }
        return undefined;
    }
    setSessionMemoryStore(store) {
        this.sessionMemoryStore = store;
    }
    getSessionMemoryStore() {
        return this.sessionMemoryStore;
    }
    setSessionLineageTracker(tracker) {
        this.sessionLineageTracker = tracker;
    }
    getSessionLineageTracker() {
        return this.sessionLineageTracker;
    }
    /**
     * Returns the conversation messages formatted for the LLM provider.
     *
     * @returns readonly reference — do not mutate; the array is shared across
     *   cache-hit callers until the next conversation mutation.
     */
    getMessagesForLLM() {
        if (this._cachedLLMMessages !== null && this._cachedLLMRevision === this._messagesRevision) {
            return this._cachedLLMMessages;
        }
        const result = [];
        for (const message of this.messages) {
            if (message.role === 'system')
                continue;
            if (message.role === 'user') {
                result.push({ role: 'user', content: message.content });
                continue;
            }
            if (message.role === 'assistant') {
                result.push({
                    role: 'assistant',
                    content: message.content,
                    ...(message.toolCalls ? { toolCalls: message.toolCalls } : {}),
                });
                continue;
            }
            result.push({
                role: 'tool',
                callId: message.callId,
                content: message.content,
                ...(message.toolName ? { name: message.toolName } : {}),
            });
        }
        this._cachedLLMMessages = result;
        this._cachedLLMRevision = this._messagesRevision;
        return result;
    }
    addUserMessage(content) {
        if (this._title === '' && typeof content === 'string' && content.trim().length > 0) {
            this.setSystemTitle(deriveConversationTitle(content));
        }
        this.messages.push({ role: 'user', content });
        // Clear undo stack when new user input is added (can't redo past new input)
        this.undoStack = [];
        this._messagesRevision++;
    }
    addAssistantMessage(content, opts) {
        this.messages.push({
            role: 'assistant',
            content,
            toolCalls: opts?.toolCalls,
            reasoningContent: opts?.reasoningContent,
            reasoningSummary: opts?.reasoningSummary,
            usage: opts?.usage,
            model: opts?.model,
            provider: opts?.provider,
        });
        this._messagesRevision++;
    }
    /**
     * undo - Remove the last complete turn (the last user message and all subsequent
     * non-user messages). Pushes the removed messages onto the undo stack.
     * Returns true if a turn was removed, false if there was nothing to undo.
     */
    undo() {
        let lastUserIdx = -1;
        for (let i = this.messages.length - 1; i >= 0; i--) {
            const msg = this.messages[i];
            if (msg?.role === 'user') {
                lastUserIdx = i;
                break;
            }
        }
        if (lastUserIdx === -1)
            return false;
        const turn = this.messages.splice(lastUserIdx);
        this.undoStack.push(turn);
        this._messagesRevision++;
        return true;
    }
    /**
     * redo - Restore the most recently undone turn.
     * Returns true if a turn was restored, false if the undo stack is empty.
     */
    redo() {
        if (this.undoStack.length === 0)
            return false;
        const turn = this.undoStack.pop();
        this.messages.push(...turn);
        this._messagesRevision++;
        return true;
    }
    addToolResults(results) {
        for (const result of results) {
            const content = result.success
                ? (result.output ?? 'Tool completed successfully.')
                : `Error: ${result.error ?? 'Unknown error'}`;
            const toolName = this.findToolName(result.callId);
            this.messages.push({
                role: 'tool',
                callId: result.callId,
                content,
                ...(toolName ? { toolName } : {}),
            });
        }
        this._messagesRevision++;
    }
    addSystemMessage(content) {
        this.messages.push({ role: 'system', content });
        this._messagesRevision++;
    }
    getLastUserMessage() {
        for (let i = this.messages.length - 1; i >= 0; i--) {
            const msg = this.messages[i];
            if (msg?.role === 'user') {
                return typeof msg.content === 'string' ? msg.content : null;
            }
        }
        return null;
    }
    getMessageCount() {
        return this.messages.length;
    }
    removeMessagesAfter(count) {
        if (count < this.messages.length) {
            this.messages.length = count;
            this._messagesRevision++;
        }
    }
    markLastUserMessageCancelled() {
        for (let i = this.messages.length - 1; i >= 0; i--) {
            const msg = this.messages[i];
            if (msg?.role === 'user') {
                msg.cancelled = true;
                this._messagesRevision++;
                return;
            }
        }
    }
    startStreamingBlock() {
        this.messages.push({ role: 'assistant', content: '' });
        this.streamingMessageIndex = this.messages.length - 1;
        this._messagesRevision++;
    }
    updateStreamingBlock(content) {
        if (this.streamingMessageIndex < 0)
            return;
        const message = this.messages[this.streamingMessageIndex];
        if (message?.role === 'assistant') {
            message.content = content;
            this._messagesRevision++;
        }
    }
    finalizeStreamingBlock() {
        if (this.streamingMessageIndex >= 0 && this.messages[this.streamingMessageIndex]?.role === 'assistant') {
            this.messages.splice(this.streamingMessageIndex, 1);
        }
        this.streamingMessageIndex = -1;
        this._messagesRevision++;
    }
    getMessageSnapshot() {
        return cloneMessages(this.messages);
    }
    getTranscriptEventIndex() {
        return buildTranscriptEventIndex(this.getMessageSnapshot());
    }
    replaceMessagesForLLM(newMessages) {
        const systemMessages = this.messages.filter((message) => message.role === 'system');
        this.messages = [...systemMessages, ...messagesToInternal(newMessages)];
        this.streamingMessageIndex = -1;
        this._messagesRevision++;
    }
    async compact(registry, modelId, trigger = 'manual', provider, context) {
        return compactConversation(this, registry, modelId, trigger, provider, context);
    }
    get title() {
        return this._title;
    }
    set title(value) {
        this._title = String(value ?? '');
        this._titleSource = this._title.trim().length > 0 ? 'user' : 'system';
    }
    getTitleSource() {
        return this._titleSource;
    }
    setSystemTitle(value) {
        if (this._titleSource === 'user')
            return;
        this._title = String(value ?? '');
        this._titleSource = 'system';
    }
    resetAll() {
        this.messages = [];
        this._title = '';
        this._titleSource = 'system';
        this.branches.clear();
        this.currentBranch = 'main';
        this.streamingMessageIndex = -1;
        this.undoStack = [];
        this._messagesRevision++;
    }
    forkBranch(name, force = false) {
        const branchName = name?.trim() || `branch-${Date.now()}`;
        if (!force && this.branches.has(branchName)) {
            return branchName;
        }
        this.branches.set(branchName, cloneMessages(this.messages));
        return branchName;
    }
    listBranches() {
        const result = [];
        const currentInMap = this.branches.has(this.currentBranch);
        if (!currentInMap) {
            result.push({ name: this.currentBranch, messageCount: this.messages.length, isCurrent: true });
        }
        for (const [name, messages] of this.branches) {
            result.push({ name, messageCount: messages.length, isCurrent: name === this.currentBranch });
        }
        return result;
    }
    switchBranch(name) {
        const stored = this.branches.get(name);
        if (!stored)
            return false;
        this.branches.set(this.currentBranch, cloneMessages(this.messages));
        this.messages = cloneMessages(stored);
        this.currentBranch = name;
        this.streamingMessageIndex = -1;
        this._messagesRevision++;
        return true;
    }
    mergeBranch(name) {
        const stored = this.branches.get(name);
        if (!stored)
            return false;
        const commonLength = Math.min(this.messages.length, stored.length);
        const toAppend = stored.slice(commonLength);
        if (toAppend.length === 0)
            return true;
        this.messages.push(...cloneMessages(toAppend));
        this._messagesRevision++;
        return true;
    }
    getCurrentBranch() {
        return this.currentBranch;
    }
    toJSON() {
        return {
            messages: cloneMessages(this.messages),
            timestamp: Date.now(),
            title: this._title,
            titleSource: this._titleSource,
            branches: cloneBranchMap(this.branches),
            currentBranch: this.currentBranch,
        };
    }
    fromJSON(data) {
        this.messages = data.messages ?? [];
        this._title = typeof data.title === 'string' ? data.title : '';
        this._titleSource = data.titleSource === 'user' || data.titleSource === 'system'
            ? data.titleSource
            : (this._title ? 'user' : 'system');
        this.branches = restoreBranchMap(data.branches);
        this.currentBranch = data.currentBranch ?? 'main';
        this.streamingMessageIndex = -1;
        this._messagesRevision++;
    }
}
export { parseDiffForApply, applyDiffContent } from './conversation-diff.js';
