/**
 * BranchManager — Owns branch creation and tree navigation.
 *
 * Extracted from AgentSession to isolate branch/fork/tree mutation logic
 * behind a narrow interface.
 */
// ============================================================================
// BranchManager
// ============================================================================
export class BranchManager {
    _deps;
    constructor(deps) {
        this._deps = deps;
    }
    /**
     * Create a fork from a specific entry.
     */
    async fork(entryId) {
        const { agent, sessionManager } = this._deps;
        const previousSessionFile = this._deps.getSessionFile();
        const selectedEntry = sessionManager.getEntry(entryId);
        if (!selectedEntry ||
            selectedEntry.type !== 'message' ||
            selectedEntry.message.role !== 'user') {
            throw new Error('Invalid entry ID for forking');
        }
        const selectedText = extractUserMessageText(selectedEntry.message.content);
        // Clear pending messages (bound to old session state)
        this._deps.clearPendingNextTurnMessages();
        if (!selectedEntry.parentId) {
            sessionManager.newSession({ parentSession: previousSessionFile });
        }
        else {
            sessionManager.createBranchedSession(selectedEntry.parentId);
        }
        const sessionId = sessionManager.getSessionId();
        agent.sessionId = sessionId;
        this._deps.observability?.setSessionId?.(sessionId);
        // Reload messages from entries (works for both file and in-memory mode)
        const sessionContext = sessionManager.buildSessionContext();
        agent.replaceMessages(sessionContext.messages);
        this._deps.observability?.info?.({
            module: 'session',
            event: 'session_forked',
            data: {
                transition: 'fork',
                parent_session_file: previousSessionFile
            }
        });
        return { selectedText, cancelled: false };
    }
    /**
     * Navigate to a different node in the session tree.
     * Unlike fork() which creates a new session file, this stays in the same file.
     */
    navigateTree(targetId, options = {}) {
        const { agent, sessionManager } = this._deps;
        const oldLeafId = sessionManager.getLeafId();
        // No-op if already at target
        if (targetId === oldLeafId) {
            return { cancelled: false };
        }
        const targetEntry = sessionManager.getEntry(targetId);
        if (!targetEntry) {
            throw new Error(`Entry ${targetId} not found`);
        }
        // Determine the new leaf position based on target type
        let newLeafId;
        let editorText;
        if (targetEntry.type === 'message' && targetEntry.message.role === 'user') {
            newLeafId = targetEntry.parentId;
            editorText = extractUserMessageText(targetEntry.message.content);
        }
        else if (targetEntry.type === 'custom_message') {
            newLeafId = targetEntry.parentId;
            editorText =
                typeof targetEntry.content === 'string'
                    ? targetEntry.content
                    : targetEntry.content
                        .filter((c) => c.type === 'text')
                        .map((c) => c.text)
                        .join('');
        }
        else {
            newLeafId = targetId;
        }
        // Switch leaf
        if (newLeafId === null) {
            sessionManager.resetLeaf();
        }
        else {
            sessionManager.branch(newLeafId);
        }
        // Attach label if provided
        if (options.label) {
            sessionManager.appendLabelChange(targetId, options.label);
        }
        // Update agent state
        const sessionContext = sessionManager.buildSessionContext();
        agent.replaceMessages(sessionContext.messages);
        this._deps.observability?.info?.({
            module: 'session',
            event: 'session_tree_navigated',
            data: {
                target_entry_id: targetId,
                leaf_changed: oldLeafId !== newLeafId
            }
        });
        return { editorText, cancelled: false };
    }
    /**
     * Get all user messages from session for fork selector.
     */
    getUserMessagesForForking() {
        const entries = this._deps.sessionManager.getEntries();
        const result = [];
        for (const entry of entries) {
            if (entry.type !== 'message')
                continue;
            if (entry.message.role !== 'user')
                continue;
            const text = extractUserMessageText(entry.message.content);
            if (text) {
                result.push({ entryId: entry.id, text });
            }
        }
        return result;
    }
}
// ============================================================================
// Utilities
// ============================================================================
/** Extract text from user message content (string or content array). */
export function extractUserMessageText(content) {
    if (typeof content === 'string')
        return content;
    if (Array.isArray(content)) {
        return content
            .filter((c) => c.type === 'text')
            .map((c) => c.text)
            .join('');
    }
    return '';
}
//# sourceMappingURL=branch-manager.js.map