import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
/**
 * @license
 * Copyright 2025 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */
import React, { useState, useEffect } from 'react';
import { Box, Text } from 'ink';
import { Colors } from '../colors.js';
import { SettingScope } from '../../config/settings.js';
import { getScopeItems, getScopeMessageForSetting, } from '../../utils/dialogScopeUtils.js';
import { RadioButtonSelect } from './shared/RadioButtonSelect.js';
import { getDialogSettingKeys, getSettingValue, setPendingSettingValue, getDisplayValue, hasRestartRequiredSettings, saveModifiedSettings, getSettingDefinition, isDefaultValue, requiresRestart, getRestartRequiredFromModified, getDefaultValue, setPendingSettingValueAny, getNestedValue, } from '../../utils/settingsUtils.js';
import { useVimMode } from '../contexts/VimModeContext.js';
import { useKeypress } from '../hooks/useKeypress.js';
import chalk from 'chalk';
import { cpSlice, cpLen, stripUnsafeCharacters } from '../utils/textUtils.js';
const maxItemsToShow = 8;
export function SettingsDialog({ settings, onSelect, onRestartRequest, }) {
    // Get vim mode context to sync vim mode changes
    const { vimEnabled, toggleVimEnabled } = useVimMode();
    // Focus state: 'settings' or 'scope'
    const [focusSection, setFocusSection] = useState('settings');
    // Scope selector state (User by default)
    const [selectedScope, setSelectedScope] = useState(SettingScope.User);
    // Active indices
    const [activeSettingIndex, setActiveSettingIndex] = useState(0);
    // Scroll offset for settings
    const [scrollOffset, setScrollOffset] = useState(0);
    const [showRestartPrompt, setShowRestartPrompt] = useState(false);
    // Local pending settings state for the selected scope
    const [pendingSettings, setPendingSettings] = useState(() => 
    // Deep clone to avoid mutation
    structuredClone(settings.forScope(selectedScope).settings));
    // Track which settings have been modified by the user
    const [modifiedSettings, setModifiedSettings] = useState(new Set());
    const [globalPendingChanges, setGlobalPendingChanges] = useState(new Map());
    // Track restart-required settings across scope changes
    const [_restartRequiredSettings, setRestartRequiredSettings] = useState(new Set());
    useEffect(() => {
        // Base settings for selected scope
        let updated = structuredClone(settings.forScope(selectedScope).settings);
        // Overlay globally pending (unsaved) changes so user sees their modifications in any scope
        const newModified = new Set();
        const newRestartRequired = new Set();
        for (const [key, value] of globalPendingChanges.entries()) {
            const def = getSettingDefinition(key);
            if (def?.type === 'boolean' && typeof value === 'boolean') {
                updated = setPendingSettingValue(key, value, updated);
            }
            else if ((def?.type === 'number' && typeof value === 'number') ||
                (def?.type === 'string' && typeof value === 'string')) {
                updated = setPendingSettingValueAny(key, value, updated);
            }
            newModified.add(key);
            if (requiresRestart(key))
                newRestartRequired.add(key);
        }
        setPendingSettings(updated);
        setModifiedSettings(newModified);
        setRestartRequiredSettings(newRestartRequired);
        setShowRestartPrompt(newRestartRequired.size > 0);
    }, [selectedScope, settings, globalPendingChanges]);
    const generateSettingsItems = () => {
        const settingKeys = getDialogSettingKeys();
        return settingKeys.map((key) => {
            const definition = getSettingDefinition(key);
            return {
                label: definition?.label || key,
                value: key,
                type: definition?.type,
                toggle: () => {
                    if (definition?.type !== 'boolean') {
                        // For non-boolean items, toggle will be handled via edit mode.
                        return;
                    }
                    const currentValue = getSettingValue(key, pendingSettings, {});
                    const newValue = !currentValue;
                    setPendingSettings((prev) => setPendingSettingValue(key, newValue, prev));
                    if (!requiresRestart(key)) {
                        const immediateSettings = new Set([key]);
                        const immediateSettingsObject = setPendingSettingValueAny(key, newValue, {});
                        console.log(`[DEBUG SettingsDialog] Saving ${key} immediately with value:`, newValue);
                        saveModifiedSettings(immediateSettings, immediateSettingsObject, settings, selectedScope);
                        // Special handling for vim mode to sync with VimModeContext
                        if (key === 'general.vimMode' && newValue !== vimEnabled) {
                            // Call toggleVimEnabled to sync the VimModeContext local state
                            toggleVimEnabled().catch((error) => {
                                console.error('Failed to toggle vim mode:', error);
                            });
                        }
                        // Remove from modifiedSettings since it's now saved
                        setModifiedSettings((prev) => {
                            const updated = new Set(prev);
                            updated.delete(key);
                            return updated;
                        });
                        // Also remove from restart-required settings if it was there
                        setRestartRequiredSettings((prev) => {
                            const updated = new Set(prev);
                            updated.delete(key);
                            return updated;
                        });
                        // Remove from global pending changes if present
                        setGlobalPendingChanges((prev) => {
                            if (!prev.has(key))
                                return prev;
                            const next = new Map(prev);
                            next.delete(key);
                            return next;
                        });
                        // Refresh pending settings from the saved state
                        setPendingSettings(structuredClone(settings.forScope(selectedScope).settings));
                    }
                    else {
                        // For restart-required settings, track as modified
                        setModifiedSettings((prev) => {
                            const updated = new Set(prev).add(key);
                            const needsRestart = hasRestartRequiredSettings(updated);
                            console.log(`[DEBUG SettingsDialog] Modified settings:`, Array.from(updated), 'Needs restart:', needsRestart);
                            if (needsRestart) {
                                setShowRestartPrompt(true);
                                setRestartRequiredSettings((prevRestart) => new Set(prevRestart).add(key));
                            }
                            return updated;
                        });
                        // Add/update pending change globally so it persists across scopes
                        setGlobalPendingChanges((prev) => {
                            const next = new Map(prev);
                            next.set(key, newValue);
                            return next;
                        });
                    }
                },
            };
        });
    };
    const items = generateSettingsItems();
    // Generic edit state
    const [editingKey, setEditingKey] = useState(null);
    const [editBuffer, setEditBuffer] = useState('');
    const [editCursorPos, setEditCursorPos] = useState(0); // Cursor position within edit buffer
    const [cursorVisible, setCursorVisible] = useState(true);
    useEffect(() => {
        if (!editingKey) {
            setCursorVisible(true);
            return;
        }
        const id = setInterval(() => setCursorVisible((v) => !v), 500);
        return () => clearInterval(id);
    }, [editingKey]);
    const startEditing = (key, initial) => {
        setEditingKey(key);
        const initialValue = initial ?? '';
        setEditBuffer(initialValue);
        setEditCursorPos(cpLen(initialValue)); // Position cursor at end of initial value
    };
    const commitEdit = (key) => {
        const definition = getSettingDefinition(key);
        const type = definition?.type;
        if (editBuffer.trim() === '' && type === 'number') {
            // Nothing entered for a number; cancel edit
            setEditingKey(null);
            setEditBuffer('');
            setEditCursorPos(0);
            return;
        }
        let parsed;
        if (type === 'number') {
            const numParsed = Number(editBuffer.trim());
            if (Number.isNaN(numParsed)) {
                // Invalid number; cancel edit
                setEditingKey(null);
                setEditBuffer('');
                setEditCursorPos(0);
                return;
            }
            parsed = numParsed;
        }
        else {
            // For strings, use the buffer as is.
            parsed = editBuffer;
        }
        // Update pending
        setPendingSettings((prev) => setPendingSettingValueAny(key, parsed, prev));
        if (!requiresRestart(key)) {
            const immediateSettings = new Set([key]);
            const immediateSettingsObject = setPendingSettingValueAny(key, parsed, {});
            saveModifiedSettings(immediateSettings, immediateSettingsObject, settings, selectedScope);
            // Remove from modified sets if present
            setModifiedSettings((prev) => {
                const updated = new Set(prev);
                updated.delete(key);
                return updated;
            });
            setRestartRequiredSettings((prev) => {
                const updated = new Set(prev);
                updated.delete(key);
                return updated;
            });
            // Remove from global pending since it's immediately saved
            setGlobalPendingChanges((prev) => {
                if (!prev.has(key))
                    return prev;
                const next = new Map(prev);
                next.delete(key);
                return next;
            });
        }
        else {
            // Mark as modified and needing restart
            setModifiedSettings((prev) => {
                const updated = new Set(prev).add(key);
                const needsRestart = hasRestartRequiredSettings(updated);
                if (needsRestart) {
                    setShowRestartPrompt(true);
                    setRestartRequiredSettings((prevRestart) => new Set(prevRestart).add(key));
                }
                return updated;
            });
            // Record pending change globally for persistence across scopes
            setGlobalPendingChanges((prev) => {
                const next = new Map(prev);
                next.set(key, parsed);
                return next;
            });
        }
        setEditingKey(null);
        setEditBuffer('');
        setEditCursorPos(0);
    };
    // Scope selector items
    const scopeItems = getScopeItems();
    const handleScopeHighlight = (scope) => {
        setSelectedScope(scope);
    };
    const handleScopeSelect = (scope) => {
        handleScopeHighlight(scope);
        setFocusSection('settings');
    };
    // Scroll logic for settings
    const visibleItems = items.slice(scrollOffset, scrollOffset + maxItemsToShow);
    // Always show arrows for consistent UI and to indicate circular navigation
    const showScrollUp = true;
    const showScrollDown = true;
    useKeypress((key) => {
        const { name, ctrl } = key;
        if (name === 'tab') {
            setFocusSection((prev) => (prev === 'settings' ? 'scope' : 'settings'));
        }
        if (focusSection === 'settings') {
            // If editing, capture input and control keys
            if (editingKey) {
                const definition = getSettingDefinition(editingKey);
                const type = definition?.type;
                if (key.paste && key.sequence) {
                    let pasted = key.sequence;
                    if (type === 'number') {
                        pasted = key.sequence.replace(/[^0-9\-+.]/g, '');
                    }
                    if (pasted) {
                        setEditBuffer((b) => {
                            const before = cpSlice(b, 0, editCursorPos);
                            const after = cpSlice(b, editCursorPos);
                            return before + pasted + after;
                        });
                        setEditCursorPos((pos) => pos + cpLen(pasted));
                    }
                    return;
                }
                if (name === 'backspace' || name === 'delete') {
                    if (name === 'backspace' && editCursorPos > 0) {
                        setEditBuffer((b) => {
                            const before = cpSlice(b, 0, editCursorPos - 1);
                            const after = cpSlice(b, editCursorPos);
                            return before + after;
                        });
                        setEditCursorPos((pos) => pos - 1);
                    }
                    else if (name === 'delete' && editCursorPos < cpLen(editBuffer)) {
                        setEditBuffer((b) => {
                            const before = cpSlice(b, 0, editCursorPos);
                            const after = cpSlice(b, editCursorPos + 1);
                            return before + after;
                        });
                        // Cursor position stays the same for delete
                    }
                    return;
                }
                if (name === 'escape') {
                    commitEdit(editingKey);
                    return;
                }
                if (name === 'return') {
                    commitEdit(editingKey);
                    return;
                }
                let ch = key.sequence;
                let isValidChar = false;
                if (type === 'number') {
                    // Allow digits, minus, plus, and dot.
                    isValidChar = /[0-9\-+.]/.test(ch);
                }
                else {
                    ch = stripUnsafeCharacters(ch);
                    // For strings, allow any single character that isn't a control
                    // sequence.
                    isValidChar = ch.length === 1;
                }
                if (isValidChar) {
                    setEditBuffer((currentBuffer) => {
                        const beforeCursor = cpSlice(currentBuffer, 0, editCursorPos);
                        const afterCursor = cpSlice(currentBuffer, editCursorPos);
                        return beforeCursor + ch + afterCursor;
                    });
                    setEditCursorPos((pos) => pos + 1);
                    return;
                }
                // Arrow key navigation
                if (name === 'left') {
                    setEditCursorPos((pos) => Math.max(0, pos - 1));
                    return;
                }
                if (name === 'right') {
                    setEditCursorPos((pos) => Math.min(cpLen(editBuffer), pos + 1));
                    return;
                }
                // Home and End keys
                if (name === 'home') {
                    setEditCursorPos(0);
                    return;
                }
                if (name === 'end') {
                    setEditCursorPos(cpLen(editBuffer));
                    return;
                }
                // Block other keys while editing
                return;
            }
            if (name === 'up' || name === 'k') {
                // If editing, commit first
                if (editingKey) {
                    commitEdit(editingKey);
                }
                const newIndex = activeSettingIndex > 0 ? activeSettingIndex - 1 : items.length - 1;
                setActiveSettingIndex(newIndex);
                // Adjust scroll offset for wrap-around
                if (newIndex === items.length - 1) {
                    setScrollOffset(Math.max(0, items.length - maxItemsToShow));
                }
                else if (newIndex < scrollOffset) {
                    setScrollOffset(newIndex);
                }
            }
            else if (name === 'down' || name === 'j') {
                // If editing, commit first
                if (editingKey) {
                    commitEdit(editingKey);
                }
                const newIndex = activeSettingIndex < items.length - 1 ? activeSettingIndex + 1 : 0;
                setActiveSettingIndex(newIndex);
                // Adjust scroll offset for wrap-around
                if (newIndex === 0) {
                    setScrollOffset(0);
                }
                else if (newIndex >= scrollOffset + maxItemsToShow) {
                    setScrollOffset(newIndex - maxItemsToShow + 1);
                }
            }
            else if (name === 'return' || name === 'space') {
                const currentItem = items[activeSettingIndex];
                if (currentItem?.type === 'number' ||
                    currentItem?.type === 'string') {
                    startEditing(currentItem.value);
                }
                else {
                    currentItem?.toggle();
                }
            }
            else if (/^[0-9]$/.test(key.sequence || '') && !editingKey) {
                const currentItem = items[activeSettingIndex];
                if (currentItem?.type === 'number') {
                    startEditing(currentItem.value, key.sequence);
                }
            }
            else if (ctrl && (name === 'c' || name === 'l')) {
                // Ctrl+C or Ctrl+L: Clear current setting and reset to default
                const currentSetting = items[activeSettingIndex];
                if (currentSetting) {
                    const defaultValue = getDefaultValue(currentSetting.value);
                    const defType = currentSetting.type;
                    if (defType === 'boolean') {
                        const booleanDefaultValue = typeof defaultValue === 'boolean' ? defaultValue : false;
                        setPendingSettings((prev) => setPendingSettingValue(currentSetting.value, booleanDefaultValue, prev));
                    }
                    else if (defType === 'number' || defType === 'string') {
                        if (typeof defaultValue === 'number' ||
                            typeof defaultValue === 'string') {
                            setPendingSettings((prev) => setPendingSettingValueAny(currentSetting.value, defaultValue, prev));
                        }
                    }
                    // Remove from modified settings since it's now at default
                    setModifiedSettings((prev) => {
                        const updated = new Set(prev);
                        updated.delete(currentSetting.value);
                        return updated;
                    });
                    // Remove from restart-required settings if it was there
                    setRestartRequiredSettings((prev) => {
                        const updated = new Set(prev);
                        updated.delete(currentSetting.value);
                        return updated;
                    });
                    // If this setting doesn't require restart, save it immediately
                    if (!requiresRestart(currentSetting.value)) {
                        const immediateSettings = new Set([currentSetting.value]);
                        const toSaveValue = currentSetting.type === 'boolean'
                            ? typeof defaultValue === 'boolean'
                                ? defaultValue
                                : false
                            : typeof defaultValue === 'number' ||
                                typeof defaultValue === 'string'
                                ? defaultValue
                                : undefined;
                        const immediateSettingsObject = toSaveValue !== undefined
                            ? setPendingSettingValueAny(currentSetting.value, toSaveValue, {})
                            : {};
                        saveModifiedSettings(immediateSettings, immediateSettingsObject, settings, selectedScope);
                        // Remove from global pending changes if present
                        setGlobalPendingChanges((prev) => {
                            if (!prev.has(currentSetting.value))
                                return prev;
                            const next = new Map(prev);
                            next.delete(currentSetting.value);
                            return next;
                        });
                    }
                    else {
                        // Track default reset as a pending change if restart required
                        if ((currentSetting.type === 'boolean' &&
                            typeof defaultValue === 'boolean') ||
                            (currentSetting.type === 'number' &&
                                typeof defaultValue === 'number') ||
                            (currentSetting.type === 'string' &&
                                typeof defaultValue === 'string')) {
                            setGlobalPendingChanges((prev) => {
                                const next = new Map(prev);
                                next.set(currentSetting.value, defaultValue);
                                return next;
                            });
                        }
                    }
                }
            }
        }
        if (showRestartPrompt && name === 'r') {
            // Only save settings that require restart (non-restart settings were already saved immediately)
            const restartRequiredSettings = getRestartRequiredFromModified(modifiedSettings);
            const restartRequiredSet = new Set(restartRequiredSettings);
            if (restartRequiredSet.size > 0) {
                saveModifiedSettings(restartRequiredSet, pendingSettings, settings, selectedScope);
                // Remove saved keys from global pending changes
                setGlobalPendingChanges((prev) => {
                    if (prev.size === 0)
                        return prev;
                    const next = new Map(prev);
                    for (const key of restartRequiredSet) {
                        next.delete(key);
                    }
                    return next;
                });
            }
            setShowRestartPrompt(false);
            setRestartRequiredSettings(new Set()); // Clear restart-required settings
            if (onRestartRequest)
                onRestartRequest();
        }
        if (name === 'escape') {
            if (editingKey) {
                commitEdit(editingKey);
            }
            else {
                onSelect(undefined, selectedScope);
            }
        }
    }, { isActive: true });
    return (_jsx(Box, { borderStyle: "round", borderColor: Colors.Gray, flexDirection: "row", padding: 1, width: "100%", height: "100%", children: _jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [_jsx(Text, { bold: true, color: Colors.AccentBlue, children: "Settings" }), _jsx(Box, { height: 1 }), showScrollUp && _jsx(Text, { color: Colors.Gray, children: "\u25B2" }), visibleItems.map((item, idx) => {
                    const isActive = focusSection === 'settings' &&
                        activeSettingIndex === idx + scrollOffset;
                    const scopeSettings = settings.forScope(selectedScope).settings;
                    const mergedSettings = settings.merged;
                    let displayValue;
                    if (editingKey === item.value) {
                        // Show edit buffer with advanced cursor highlighting
                        if (cursorVisible && editCursorPos < cpLen(editBuffer)) {
                            // Cursor is in the middle or at start of text
                            const beforeCursor = cpSlice(editBuffer, 0, editCursorPos);
                            const atCursor = cpSlice(editBuffer, editCursorPos, editCursorPos + 1);
                            const afterCursor = cpSlice(editBuffer, editCursorPos + 1);
                            displayValue =
                                beforeCursor + chalk.inverse(atCursor) + afterCursor;
                        }
                        else if (cursorVisible && editCursorPos >= cpLen(editBuffer)) {
                            // Cursor is at the end - show inverted space
                            displayValue = editBuffer + chalk.inverse(' ');
                        }
                        else {
                            // Cursor not visible
                            displayValue = editBuffer;
                        }
                    }
                    else if (item.type === 'number' || item.type === 'string') {
                        // For numbers/strings, get the actual current value from pending settings
                        const path = item.value.split('.');
                        const currentValue = getNestedValue(pendingSettings, path);
                        const defaultValue = getDefaultValue(item.value);
                        if (currentValue !== undefined && currentValue !== null) {
                            displayValue = String(currentValue);
                        }
                        else {
                            displayValue =
                                defaultValue !== undefined && defaultValue !== null
                                    ? String(defaultValue)
                                    : '';
                        }
                        // Add * if value differs from default OR if currently being modified
                        const isModified = modifiedSettings.has(item.value);
                        const effectiveCurrentValue = currentValue !== undefined && currentValue !== null
                            ? currentValue
                            : defaultValue;
                        const isDifferentFromDefault = effectiveCurrentValue !== defaultValue;
                        if (isDifferentFromDefault || isModified) {
                            displayValue += '*';
                        }
                    }
                    else {
                        // For booleans and other types, use existing logic
                        displayValue = getDisplayValue(item.value, scopeSettings, mergedSettings, modifiedSettings, pendingSettings);
                    }
                    const shouldBeGreyedOut = isDefaultValue(item.value, scopeSettings);
                    // Generate scope message for this setting
                    const scopeMessage = getScopeMessageForSetting(item.value, selectedScope, settings);
                    return (_jsxs(React.Fragment, { children: [_jsxs(Box, { flexDirection: "row", alignItems: "center", children: [_jsx(Box, { minWidth: 2, flexShrink: 0, children: _jsx(Text, { color: isActive ? Colors.AccentGreen : Colors.Gray, children: isActive ? '●' : '' }) }), _jsx(Box, { minWidth: 50, children: _jsxs(Text, { color: isActive ? Colors.AccentGreen : Colors.Foreground, children: [item.label, scopeMessage && (_jsxs(Text, { color: Colors.Gray, children: [" ", scopeMessage] }))] }) }), _jsx(Box, { minWidth: 3 }), _jsx(Text, { color: isActive
                                            ? Colors.AccentGreen
                                            : shouldBeGreyedOut
                                                ? Colors.Gray
                                                : Colors.Foreground, children: displayValue })] }), _jsx(Box, { height: 1 })] }, item.value));
                }), showScrollDown && _jsx(Text, { color: Colors.Gray, children: "\u25BC" }), _jsx(Box, { height: 1 }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsxs(Text, { bold: focusSection === 'scope', wrap: "truncate", children: [focusSection === 'scope' ? '> ' : '  ', "Apply To"] }), _jsx(RadioButtonSelect, { items: scopeItems, initialIndex: 0, onSelect: handleScopeSelect, onHighlight: handleScopeHighlight, isFocused: focusSection === 'scope', showNumbers: focusSection === 'scope' })] }), _jsx(Box, { height: 1 }), _jsx(Text, { color: Colors.Gray, children: "(Use Enter to select, Tab to change focus)" }), showRestartPrompt && (_jsx(Text, { color: Colors.AccentYellow, children: "To see changes, Gemini CLI must be restarted. Press r to exit and apply changes now." }))] }) }));
}
//# sourceMappingURL=SettingsDialog.js.map