const DEFAULT_MAX_PROMPT_HISTORY_ENTRIES = 100;
const DEFAULT_MAX_PROMPT_UNDO_STATES = 100;
/**
 * Owns interface-neutral prompt draft and prompt history semantics.
 *
 * Interfaces own key-event capture and rendering. This service owns the pure
 * text-editing and prompt-history rules so web-v2 and cli-v2 do not drift on
 * which submitted prompts are recorded or how history navigation restores a
 * draft that was in progress before browsing history.
 */
export class ClientSharedPromptInputService {
    static parseDirectShellDraft(value) {
        const trimmed = value.trim();
        if (!trimmed.startsWith('!')) {
            return undefined;
        }
        const command = trimmed.slice(1).trim();
        return { command };
    }
    static clampCursor(value, cursor) {
        return Math.min(Math.max(0, cursor), value.length);
    }
    static insertText(state, input) {
        const cursor = this.clampCursor(state.value, state.cursor);
        return {
            value: `${state.value.slice(0, cursor)}${input}${state.value.slice(cursor)}`,
            cursor: cursor + input.length,
        };
    }
    static deletePreviousCharacter(state) {
        const cursor = this.clampCursor(state.value, state.cursor);
        if (cursor === 0) {
            return { value: state.value, cursor };
        }
        return {
            value: this.removeRange(state.value, cursor - 1, cursor),
            cursor: cursor - 1,
        };
    }
    static deletePreviousWord(state) {
        const cursor = this.clampCursor(state.value, state.cursor);
        const nextCursor = this.findPreviousWordBoundary(state.value, cursor);
        return {
            value: this.removeRange(state.value, nextCursor, cursor),
            cursor: nextCursor,
        };
    }
    static deleteBeforeCursor(state) {
        const cursor = this.clampCursor(state.value, state.cursor);
        return {
            value: state.value.slice(cursor),
            cursor: 0,
        };
    }
    static deleteAfterCursor(state) {
        const cursor = this.clampCursor(state.value, state.cursor);
        return {
            value: state.value.slice(0, cursor),
            cursor,
        };
    }
    static moveCursor(state, direction) {
        const cursor = this.clampCursor(state.value, state.cursor);
        const movements = {
            start: () => 0,
            end: () => state.value.length,
            previousCharacter: () => Math.max(0, cursor - 1),
            nextCharacter: () => Math.min(state.value.length, cursor + 1),
            previousWord: () => this.findPreviousWordBoundary(state.value, cursor),
            nextWord: () => this.findNextWordBoundary(state.value, cursor),
        };
        return movements[direction]();
    }
    static recordPrompt(state, value, maxEntries = DEFAULT_MAX_PROMPT_HISTORY_ENTRIES) {
        const trimmed = value.trim();
        if (!trimmed) {
            return state;
        }
        return {
            entries: [...state.entries.filter((entry) => entry !== trimmed), trimmed].slice(-maxEntries),
            index: undefined,
            savedDraft: undefined,
        };
    }
    static navigateHistory(args) {
        const { entries, index, savedDraft } = args.state;
        if (entries.length === 0) {
            return undefined;
        }
        if (args.direction === 'previous') {
            const nextIndex = index === undefined ? entries.length - 1 : Math.max(0, index - 1);
            const value = entries[nextIndex] ?? '';
            return {
                history: {
                    entries,
                    index: nextIndex,
                    savedDraft: savedDraft ?? args.currentDraft,
                },
                draft: { value, cursor: value.length },
            };
        }
        if (index === undefined) {
            return undefined;
        }
        if (index < entries.length - 1) {
            const nextIndex = index + 1;
            const value = entries[nextIndex] ?? '';
            return {
                history: {
                    entries,
                    index: nextIndex,
                    savedDraft,
                },
                draft: { value, cursor: value.length },
            };
        }
        return {
            history: {
                entries,
                index: undefined,
                savedDraft: undefined,
            },
            draft: savedDraft ?? { value: '', cursor: 0 },
        };
    }
    static canNavigateHistory(direction, state) {
        if (!state.value.includes('\n')) {
            return true;
        }
        if (direction === 'previous') {
            const firstLineEnd = state.value.indexOf('\n');
            return state.cursor <= firstLineEnd;
        }
        const lastLineStart = state.value.lastIndexOf('\n') + 1;
        return state.cursor >= lastLineStart;
    }
    static recordUndoState(state, currentDraft, nextDraft, maxStates = DEFAULT_MAX_PROMPT_UNDO_STATES) {
        if (this.areDraftsEqual(currentDraft, nextDraft)) {
            return state;
        }
        return {
            undoStack: [...state.undoStack, currentDraft].slice(-maxStates),
            redoStack: [],
        };
    }
    static undoPromptEdit(state, currentDraft) {
        const previous = state.undoStack.at(-1);
        if (!previous) {
            return undefined;
        }
        return {
            history: {
                undoStack: state.undoStack.slice(0, -1),
                redoStack: [...state.redoStack, currentDraft],
            },
            draft: previous,
        };
    }
    static redoPromptEdit(state, currentDraft) {
        const next = state.redoStack.at(-1);
        if (!next) {
            return undefined;
        }
        return {
            history: {
                undoStack: [...state.undoStack, currentDraft],
                redoStack: state.redoStack.slice(0, -1),
            },
            draft: next,
        };
    }
    static clearUndoRedo() {
        return { undoStack: [], redoStack: [] };
    }
    static removeRange(value, start, end) {
        return `${value.slice(0, start)}${value.slice(end)}`;
    }
    static areDraftsEqual(left, right) {
        return left.value === right.value && left.cursor === right.cursor;
    }
    static findPreviousWordBoundary(value, cursor) {
        let index = cursor;
        while (index > 0 && isWordBoundary(value[index - 1])) {
            index--;
        }
        while (index > 0 && !isWordBoundary(value[index - 1])) {
            index--;
        }
        return index;
    }
    static findNextWordBoundary(value, cursor) {
        let index = cursor;
        while (index < value.length && isWordBoundary(value[index])) {
            index++;
        }
        while (index < value.length && !isWordBoundary(value[index])) {
            index++;
        }
        return index;
    }
}
function isWordBoundary(char) {
    return !char || /\s|[.,/#!$%^&*;:{}=\-_`~()\[\]"'<>?\\|]/.test(char);
}
//# sourceMappingURL=prompt-input-service.js.map