import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
import { Box, Text, useFocus, useInput } from 'ink';
import Spinner from 'ink-spinner';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { commandRegistry } from '../commands.js';
import { DevelopmentModeIndicator } from '../components/development-mode-indicator.js';
import TextInput from '../components/text-input.js';
import { useInputState } from '../hooks/useInputState.js';
import { useResponsiveTerminal } from '../hooks/useTerminalWidth.js';
import { useTheme } from '../hooks/useTheme.js';
import { useUIStateContext } from '../hooks/useUIState.js';
import { promptHistory } from '../prompt-history.js';
import { extractImageReferences, readClipboardImage, readImageFile, } from '../utils/clipboard-image.js';
import { getCurrentFileMention, getFileCompletions, } from '../utils/file-autocomplete.js';
import { handleFileMention } from '../utils/file-mention-handler.js';
import { assemblePrompt } from '../utils/prompt-processor.js';
export default function UserInput({ onSubmit, placeholder, customCommands = [], disabled = false, isBusy = false, onToggleMode, onToggleReasoningExpanded, onToggleCompactDisplay, compactToolDisplay = true, developmentMode = 'normal', contextPercentUsed, contextSource, sessionName, tune, currentModel, activeEditor, onDismissActiveEditor, forceFocus = false, }) {
    const { isFocused, focus } = useFocus({ autoFocus: !disabled, id: 'user-input' });
    const effectiveFocus = forceFocus || isFocused;
    const { colors } = useTheme();
    const inputState = useInputState();
    const uiState = useUIStateContext();
    const { boxWidth, isNarrow } = useResponsiveTerminal();
    const [textInputKey, setTextInputKey] = useState(0);
    const completionJustSelectedRef = useRef(false);
    // Store the full InputState draft when starting history navigation, so it can be restored
    const savedDraftRef = useRef({
        displayValue: '',
        placeholderContent: {},
    });
    // File autocomplete state
    const [isFileAutocompleteMode, setIsFileAutocompleteMode] = useState(false);
    const [fileCompletions, setFileCompletions] = useState([]);
    const [selectedFileIndex, setSelectedFileIndex] = useState(0);
    // Pending image attachments sent with the next submitted message.
    const [attachments, setAttachments] = useState([]);
    const { input, historyIndex, setOriginalInput, setHistoryIndex, updateInput, resetInput, deletePlaceholder: _deletePlaceholder, currentState, setInputState, } = inputState;
    const { showClearMessage, showCompletions, completions, pendingFileMentions, selectedCompletionIndex, setShowClearMessage, setShowCompletions, setCompletions, setPendingFileMentions, setSelectedCompletionIndex, resetUIState, } = uiState;
    // Check if we're in bash mode (input starts with !)
    const isBashMode = input.trim().startsWith('!');
    // Check if we're in command mode (input starts with /)
    const isCommandMode = input.trim().startsWith('/');
    // Load history on mount
    useEffect(() => {
        void promptHistory.loadHistory();
    }, []);
    // Consume pending file mentions from explorer and insert into input
    // Properly attach files by calling handleFileMention for each
    useEffect(() => {
        if (pendingFileMentions.length === 0)
            return;
        const attachFiles = async () => {
            let state = currentState;
            let displayValue = state.displayValue;
            for (const filePath of pendingFileMentions) {
                // Create a temporary mention text to replace
                const mentionText = `@${filePath}`;
                // Add the mention to display value first
                displayValue = displayValue
                    ? `${displayValue} ${mentionText}`
                    : mentionText;
                // Handle the file mention to create placeholder
                const result = await handleFileMention(filePath, displayValue, state.placeholderContent, mentionText);
                if (result) {
                    state = result;
                    displayValue = result.displayValue;
                }
            }
            setInputState(state);
            setTextInputKey(prev => prev + 1);
            setPendingFileMentions([]);
        };
        void attachFiles();
    }, [
        pendingFileMentions,
        currentState,
        setInputState,
        setPendingFileMentions,
    ]);
    // Trigger file autocomplete when input changes
    useEffect(() => {
        const runFileAutocomplete = async () => {
            const mention = getCurrentFileMention(input, input.length);
            if (mention) {
                setIsFileAutocompleteMode(true);
                const cwd = process.cwd();
                const completions = await getFileCompletions(mention.mention, cwd);
                setFileCompletions(completions);
                setSelectedFileIndex(0); // Reset selection when completions change
            }
            else {
                setIsFileAutocompleteMode(false);
                setFileCompletions([]);
                setSelectedFileIndex(0);
            }
        };
        void runFileAutocomplete();
    }, [input]);
    // Calculate command completions using useMemo to prevent flashing
    const commandCompletions = useMemo(() => {
        if (!isCommandMode || isFileAutocompleteMode) {
            return [];
        }
        // Once the user types a space, they're entering arguments for the
        // command (e.g. `/model gpt-4`). Stop offering completions so Enter
        // submits the command-with-args instead of selecting a completion and
        // dropping everything after the command name.
        if (input.slice(1).includes(' ')) {
            return [];
        }
        const commandPrefix = input.slice(1).split(' ')[0];
        const builtInCompletions = commandRegistry.getCompletions(commandPrefix);
        const customCompletions = customCommands
            .filter(cmd => {
            // Include all when no prefix, otherwise filter by prefix
            return (!commandPrefix ||
                cmd.toLowerCase().includes(commandPrefix.toLowerCase()));
        })
            .sort((a, b) => a.localeCompare(b));
        return [
            ...builtInCompletions.map(cmd => ({ name: cmd, isCustom: false })),
            ...customCompletions.map(cmd => ({ name: cmd, isCustom: true })),
        ];
    }, [input, isCommandMode, isFileAutocompleteMode, customCommands]);
    // Update UI state for command completions
    useEffect(() => {
        if (completionJustSelectedRef.current) {
            completionJustSelectedRef.current = false;
            return;
        }
        if (commandCompletions.length > 0) {
            setCompletions(commandCompletions);
            setShowCompletions(true);
            setSelectedCompletionIndex(0);
        }
        else if (showCompletions) {
            setCompletions([]);
            setShowCompletions(false);
            setSelectedCompletionIndex(-1);
        }
    }, [
        commandCompletions,
        showCompletions,
        setCompletions,
        setShowCompletions,
        setSelectedCompletionIndex,
    ]);
    // Helper functions
    // Handle file mention selection (Tab key in file autocomplete mode)
    const handleFileSelection = useCallback(async () => {
        if (!isFileAutocompleteMode || fileCompletions.length === 0) {
            return false;
        }
        const mention = getCurrentFileMention(input, input.length);
        if (!mention) {
            return false;
        }
        // Select the currently highlighted file
        const selectedPath = fileCompletions[selectedFileIndex]?.path;
        if (!selectedPath) {
            return false;
        }
        // Extract the original mention text (the @... part we're replacing)
        const mentionText = input.substring(mention.startIndex, mention.endIndex);
        // Handle the file mention to create placeholder
        const result = await handleFileMention(selectedPath, currentState.displayValue, currentState.placeholderContent, mentionText);
        if (result) {
            setInputState(result);
            setIsFileAutocompleteMode(false);
            setFileCompletions([]);
            setSelectedFileIndex(0);
            setTextInputKey(prev => prev + 1);
            return true;
        }
        return false;
    }, [
        isFileAutocompleteMode,
        fileCompletions,
        selectedFileIndex,
        input,
        currentState,
        setInputState,
    ]);
    // Attach an image to the pending message. We never gate on a model-capability
    // heuristic here: if the model can't see images it will say so or error, which
    // is clearer than an over-cautious warning on every attach.
    const attachImage = useCallback((image) => {
        setAttachments(prev => [...prev, image]);
    }, []);
    // Handle form submission
    const handleSubmit = useCallback(() => {
        if (!onSubmit)
            return;
        let images = attachments;
        let assembled = assemblePrompt(currentState);
        let display = currentState.displayValue;
        // Image file paths the user typed, pasted, or dragged into the terminal
        // (often quoted, mixed in with prose) become attachments and are stripped
        // from the message text rather than sent as literal paths.
        const { text: cleanedAssembled, paths } = extractImageReferences(assembled);
        if (paths.length > 0) {
            const dropped = paths
                .map(readImageFile)
                .filter((img) => img !== null);
            if (dropped.length > 0) {
                images = [...attachments, ...dropped];
                assembled = cleanedAssembled;
                display = extractImageReferences(display).text;
            }
        }
        // Nothing to send: no text and no attachments.
        if (!assembled.trim() && images.length === 0)
            return;
        // Save the InputState to history and send assembled message to AI
        promptHistory.addPrompt(currentState);
        onSubmit(assembled, display, images.length > 0 ? images : undefined);
        resetInput();
        resetUIState();
        setAttachments([]);
        promptHistory.resetIndex();
    }, [attachments, onSubmit, resetInput, resetUIState, currentState]);
    // Handle escape key logic
    const handleEscape = useCallback(() => {
        if (showClearMessage) {
            resetInput();
            resetUIState();
            setAttachments([]);
            onDismissActiveEditor?.();
            focus('user-input');
        }
        else {
            setShowClearMessage(true);
        }
    }, [
        showClearMessage,
        resetInput,
        resetUIState,
        onDismissActiveEditor,
        setShowClearMessage,
        focus,
    ]);
    // History navigation
    const handleHistoryNavigation = useCallback((direction) => {
        const history = promptHistory.getHistory();
        if (history.length === 0)
            return;
        if (direction === 'up') {
            if (historyIndex === -1) {
                // Save the full current state before starting navigation
                savedDraftRef.current = currentState;
                setOriginalInput(input);
                setHistoryIndex(history.length - 1);
                setInputState(history[history.length - 1]);
                setTextInputKey(prev => prev + 1);
            }
            else if (historyIndex > 0) {
                const newIndex = historyIndex - 1;
                setHistoryIndex(newIndex);
                setInputState(history[newIndex]);
                setTextInputKey(prev => prev + 1);
            }
            else if (historyIndex === 0) {
                // At first history item, restore saved draft
                setHistoryIndex(-2);
                setInputState(savedDraftRef.current);
                setTextInputKey(prev => prev + 1);
            }
            else if (historyIndex === -2) {
                // At draft, cycle back to last history item
                savedDraftRef.current = currentState;
                setHistoryIndex(history.length - 1);
                setInputState(history[history.length - 1]);
                setTextInputKey(prev => prev + 1);
            }
        }
        else {
            if (historyIndex === -1) {
                // Save draft, go to draft cycling state (visually a no-op)
                savedDraftRef.current = currentState;
                setOriginalInput(input);
                setHistoryIndex(-2);
                setInputState(savedDraftRef.current);
                setTextInputKey(prev => prev + 1);
            }
            else if (historyIndex === -2) {
                // At draft, cycle to first history item
                savedDraftRef.current = currentState;
                setHistoryIndex(0);
                setInputState(history[0]);
                setTextInputKey(prev => prev + 1);
            }
            else if (historyIndex >= 0 && historyIndex < history.length - 1) {
                // Move forward in history
                const newIndex = historyIndex + 1;
                setHistoryIndex(newIndex);
                setInputState(history[newIndex]);
                setTextInputKey(prev => prev + 1);
            }
            else if (historyIndex === history.length - 1) {
                // At last history item, restore saved draft
                setHistoryIndex(-2);
                setInputState(savedDraftRef.current);
                setTextInputKey(prev => prev + 1);
            }
        }
    }, [
        historyIndex,
        input,
        currentState,
        setHistoryIndex,
        setOriginalInput,
        setInputState,
    ]);
    useInput((inputChar, key) => {
        // Cancelling in-flight work is owned by the single section-level Escape
        // handler (see InteractiveApp), which fires no matter which component is
        // mounted. Here we only swallow Escape while busy so it doesn't fall
        // through to the clear-input double-press.
        if (key.escape && (isBusy || disabled)) {
            return;
        }
        // Handle shift+tab to toggle development mode (always available)
        if (key.tab && key.shift && onToggleMode) {
            onToggleMode();
            return;
        }
        // Handle ctrl+o to toggle compact tool display (always available)
        if (key.ctrl && inputChar === 'o' && onToggleCompactDisplay) {
            onToggleCompactDisplay();
            return;
        }
        // Handle ctrl+r to toggle expanded reasoning traces (always available)
        if (key.ctrl && inputChar === 'r' && onToggleReasoningExpanded) {
            onToggleReasoningExpanded();
            return;
        }
        // Block all other input when disabled
        if (disabled) {
            return;
        }
        // Ctrl+V: pull an image off the system clipboard as an attachment.
        // Terminal paste of regular text arrives as a bracketed paste, not as
        // Ctrl+V, so this binding is free to mean "paste image".
        if (key.ctrl && inputChar === 'v') {
            const image = readClipboardImage();
            if (image) {
                attachImage(image);
            }
            return;
        }
        // Ctrl+X: drop the most recently added image attachment.
        if (key.ctrl && inputChar === 'x') {
            setAttachments(prev => prev.slice(0, -1));
            return;
        }
        // Handle special keys
        if (key.escape) {
            handleEscape();
            return;
        }
        // Handle Tab key
        if (key.tab) {
            // File autocomplete takes priority
            if (isFileAutocompleteMode) {
                void handleFileSelection();
                return;
            }
            // Command completion - use pre-calculated commandCompletions
            if (input.startsWith('/')) {
                // Don't auto-complete on Tab when completions list is visible - use Enter to select
                if (showCompletions && completions.length > 0) {
                    return;
                }
                if (commandCompletions.length === 1) {
                    // Auto-complete when there's exactly one match
                    const completion = commandCompletions[0];
                    const completedText = `/${completion.name}`;
                    // Use setInputState to bypass paste detection for autocomplete
                    setInputState({
                        displayValue: completedText,
                        placeholderContent: currentState.placeholderContent,
                    });
                    setTextInputKey(prev => prev + 1);
                }
                else if (commandCompletions.length > 1) {
                    // Show completions when there are multiple matches
                    setCompletions(commandCompletions);
                    setShowCompletions(true);
                }
                return;
            }
        }
        // Space exits file autocomplete mode
        if (inputChar === ' ' && isFileAutocompleteMode) {
            setIsFileAutocompleteMode(false);
            setFileCompletions([]);
        }
        // Clear clear message on other input
        if (showClearMessage) {
            setShowClearMessage(false);
            focus('user-input');
        }
        // Handle return keys for multiline input
        // Ctrl+J is the official newline shortcut and reliably sends a literal LF
        if ((key.ctrl && inputChar === 'j') ||
            (inputChar === '\n' && !key.return)) {
            updateInput(input + '\n');
            return;
        }
        // Support Shift+Enter if the terminal sends it properly
        if (key.return && key.shift) {
            updateInput(input + '\n');
            return;
        }
        // Handle Enter to select completion
        if (key.return &&
            !key.shift &&
            showCompletions &&
            completions.length > 0 &&
            selectedCompletionIndex >= 0) {
            const selected = completions[selectedCompletionIndex];
            const completedText = `/${selected.name}`;
            completionJustSelectedRef.current = true;
            setInputState({
                displayValue: completedText,
                placeholderContent: {},
            });
            setShowCompletions(false);
            setSelectedCompletionIndex(-1);
            setTextInputKey(prev => prev + 1);
            return;
        }
        // Handle Enter to submit (fallthrough - if completion handler didn't return)
        if (key.return && !key.shift) {
            handleSubmit();
            return;
        }
        // Handle navigation
        if (key.upArrow) {
            // File autocomplete navigation takes priority
            if (isFileAutocompleteMode && fileCompletions.length > 0) {
                setSelectedFileIndex(prev => prev > 0 ? prev - 1 : fileCompletions.length - 1);
                return;
            }
            // Command completion navigation takes priority over history
            if (showCompletions && completions.length > 0) {
                setSelectedCompletionIndex(prev => prev > 0 ? prev - 1 : completions.length - 1);
                return;
            }
            handleHistoryNavigation('up');
            return;
        }
        if (key.downArrow) {
            // File autocomplete navigation takes priority
            if (isFileAutocompleteMode && fileCompletions.length > 0) {
                setSelectedFileIndex(prev => prev < fileCompletions.length - 1 ? prev + 1 : 0);
                return;
            }
            // Command completion navigation takes priority over history
            if (showCompletions && completions.length > 0) {
                setSelectedCompletionIndex(prev => prev < completions.length - 1 ? prev + 1 : 0);
                return;
            }
            handleHistoryNavigation('down');
            return;
        }
    });
    const textColor = disabled || !input ? colors.secondary : colors.primary;
    // When disabled, show minimal UI to avoid cluttering the screen
    if (disabled) {
        return (_jsxs(Box, { flexDirection: "column", paddingY: 1, width: "100%", marginTop: 1, children: [_jsxs(Text, { color: colors.secondary, children: [_jsx(Spinner, { type: "dots" }), " Press Esc to cancel", onToggleCompactDisplay && (_jsxs(Text, { children: [' ', "\u00B7 ctrl-o ", compactToolDisplay ? 'expand' : 'compact', ' ', isNarrow ? '' : 'tool results'] }))] }), _jsx(DevelopmentModeIndicator, { developmentMode: developmentMode, colors: colors, contextPercentUsed: contextPercentUsed ?? null, contextSource: contextSource ?? null, sessionName: sessionName, tune: tune, currentModel: currentModel })] }));
    }
    return (_jsxs(_Fragment, { children: [!isBashMode ? (_jsx(Text, { color: colors.primary, bold: true, children: "What would you like me to help with?" })) : (_jsx(Text, { color: colors.tool, bold: true, children: "Bash mode" })), _jsxs(Box, { flexDirection: "column", marginTop: 1, backgroundColor: colors.base, width: boxWidth, padding: 1, borderStyle: "bold", borderLeft: true, borderRight: false, borderTop: false, borderBottom: false, borderLeftColor: isBashMode ? colors.tool : colors.primary, children: [_jsxs(Box, { children: [input.length === 0 && (_jsxs(Text, { color: isBashMode ? colors.tool : textColor, children: ['>', " "] })), _jsx(TextInput, { value: input, onChange: updateInput, onSubmit: handleSubmit, onEnter: handleSubmit, placeholder: "/ commands, ! bash, \u2191/\u2193 history", focus: effectiveFocus, wrapWidth: boxWidth - 3, handleEnter: false }, textInputKey)] }), showClearMessage && (_jsx(Text, { color: colors.secondary, children: "Press escape again to clear" })), showCompletions && completions.length > 0 && (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { color: colors.secondary, children: "Available commands:" }), completions.map((completion, index) => {
                                const isSelected = index === selectedCompletionIndex;
                                return (_jsxs(Text, { color: isSelected
                                        ? colors.info
                                        : completion.isCustom
                                            ? colors.info
                                            : colors.primary, bold: isSelected, children: [isSelected ? '▸ ' : '  ', "/", completion.name] }, index));
                            })] })), isFileAutocompleteMode && fileCompletions.length > 0 && (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { color: colors.secondary, children: "File suggestions (\u2191/\u2193 to navigate, Tab to select):" }), fileCompletions.slice(0, 5).map((file, index) => (_jsxs(Text, { color: index === selectedFileIndex ? colors.info : colors.primary, bold: index === selectedFileIndex, children: [index === selectedFileIndex ? '▸ ' : '  ', file.path] }, index)))] }))] }), attachments.length > 0 && (_jsxs(Box, { marginTop: 1, children: [_jsx(Text, { color: colors.info, children: attachments
                            .map((img, i) => `[image #${i + 1}: ${img.source ?? 'image'}]`)
                            .join(' ') }), _jsx(Text, { color: colors.secondary, children: " \u00B7 ctrl-x remove last" })] })), _jsx(DevelopmentModeIndicator, { developmentMode: developmentMode, colors: colors, contextPercentUsed: contextPercentUsed ?? null, contextSource: contextSource ?? null, sessionName: sessionName, tune: tune, currentModel: currentModel, activeEditor: activeEditor })] }));
}
//# sourceMappingURL=user-input.js.map