/**
 * @license
 * Copyright 2025 Vybestack LLC
 * SPDX-License-Identifier: Apache-2.0
 */
import { useCallback, useMemo, useEffect, useState } from 'react';
import process from 'node:process';
import { GitService, Logger, logSlashCommand, SlashCommandEvent, ToolConfirmationOutcome, Storage, } from '@vybestack/llxprt-code-core';
import { useSessionStats } from '../contexts/SessionContext.js';
import { MessageType, } from '../types.js';
import { CommandService } from '../../services/CommandService.js';
import { BuiltinCommandLoader } from '../../services/BuiltinCommandLoader.js';
import { FileCommandLoader } from '../../services/FileCommandLoader.js';
import { McpPromptLoader } from '../../services/McpPromptLoader.js';
import { secureInputHandler } from '../utils/secureInputHandler.js';
import { runExitCleanup } from '../../utils/cleanup.js';
/**
 * Hook to define and process slash commands (e.g., /help, /clear).
 */
export const useSlashCommandProcessor = (config, settings, addItem, clearItems, loadHistory, refreshStatic, onDebugMessage, openThemeDialog, openAuthDialog, openEditorDialog, openProviderDialog, openProviderModelDialog, openLoadProfileDialog, openToolsDialog, toggleCorgiMode, setQuittingMessages, openPrivacyNotice, openSettingsDialog, toggleVimEnabled, setIsProcessing, setLlxprtMdFileCount) => {
    const session = useSessionStats();
    const [commands, setCommands] = useState([]);
    const [reloadTrigger, setReloadTrigger] = useState(0);
    const reloadCommands = useCallback(() => {
        setReloadTrigger((v) => v + 1);
    }, []);
    const [shellConfirmationRequest, setShellConfirmationRequest] = useState(null);
    const [confirmationRequest, setConfirmationRequest] = useState(null);
    const [sessionShellAllowlist, setSessionShellAllowlist] = useState(new Set());
    const gitService = useMemo(() => {
        if (!config?.getProjectRoot()) {
            return;
        }
        return new GitService(config.getProjectRoot(), config.storage);
    }, [config]);
    const logger = useMemo(() => {
        const l = new Logger(config?.getSessionId() || '', config?.storage ?? new Storage(process.cwd()));
        // The logger's initialize is async, but we can create the instance
        // synchronously. Commands that use it will await its initialization.
        return l;
    }, [config]);
    const [pendingCompressionItem, setPendingCompressionItem] = useState(null);
    const pendingHistoryItems = useMemo(() => {
        const items = [];
        if (pendingCompressionItem != null) {
            items.push(pendingCompressionItem);
        }
        return items;
    }, [pendingCompressionItem]);
    const addMessage = useCallback((message) => {
        // Convert Message to HistoryItemWithoutId
        let historyItemContent;
        if (message.type === MessageType.ABOUT) {
            historyItemContent = {
                type: 'about',
                cliVersion: message.cliVersion,
                osVersion: message.osVersion,
                sandboxEnv: message.sandboxEnv,
                modelVersion: message.modelVersion,
                selectedAuthType: message.selectedAuthType,
                gcpProject: message.gcpProject,
                keyfile: message.keyfile,
                key: message.key,
                ideClient: message.ideClient,
            };
        }
        else if (message.type === MessageType.HELP) {
            historyItemContent = {
                type: 'help',
                timestamp: message.timestamp,
            };
        }
        else if (message.type === MessageType.STATS) {
            historyItemContent = {
                type: 'stats',
                duration: message.duration,
            };
        }
        else if (message.type === MessageType.MODEL_STATS) {
            historyItemContent = {
                type: 'model_stats',
            };
        }
        else if (message.type === MessageType.TOOL_STATS) {
            historyItemContent = {
                type: 'tool_stats',
            };
        }
        else if (message.type === MessageType.QUIT) {
            historyItemContent = {
                type: 'quit',
                duration: message.duration,
            };
        }
        else if (message.type === MessageType.COMPRESSION) {
            historyItemContent = {
                type: 'compression',
                compression: message.compression,
            };
        }
        else {
            historyItemContent = {
                type: message.type,
                text: message.content,
            };
        }
        addItem(historyItemContent, message.timestamp.getTime());
    }, [addItem]);
    const commandContext = useMemo(() => ({
        services: {
            config,
            settings,
            git: gitService,
            logger,
        },
        ui: {
            addItem,
            clear: () => {
                clearItems();
                console.clear();
                refreshStatic();
            },
            loadHistory,
            setDebugMessage: onDebugMessage,
            pendingItem: pendingCompressionItem,
            setPendingItem: setPendingCompressionItem,
            toggleCorgiMode,
            toggleVimEnabled,
            setLlxprtMdFileCount,
            updateHistoryTokenCount: session.updateHistoryTokenCount,
            reloadCommands,
        },
        session: {
            stats: session.stats,
            sessionShellAllowlist,
        },
    }), [
        config,
        settings,
        gitService,
        logger,
        loadHistory,
        addItem,
        clearItems,
        session.updateHistoryTokenCount,
        refreshStatic,
        session.stats,
        onDebugMessage,
        pendingCompressionItem,
        setPendingCompressionItem,
        toggleCorgiMode,
        toggleVimEnabled,
        sessionShellAllowlist,
        setLlxprtMdFileCount,
        reloadCommands,
    ]);
    useEffect(() => {
        if (!config) {
            return;
        }
        const ideClient = config.getIdeClient();
        if (!ideClient) {
            return;
        }
        const listener = () => {
            reloadCommands();
        };
        ideClient.addStatusChangeListener(listener);
        return () => {
            ideClient.removeStatusChangeListener(listener);
        };
    }, [config, reloadCommands]);
    useEffect(() => {
        const controller = new AbortController();
        const load = async () => {
            const loaders = [
                new McpPromptLoader(config),
                new BuiltinCommandLoader(config),
                new FileCommandLoader(config),
            ];
            const commandService = await CommandService.create(loaders, controller.signal);
            // Only update commands if not aborted
            if (!controller.signal.aborted) {
                setCommands(commandService.getCommands());
            }
        };
        load();
        return () => {
            controller.abort();
        };
    }, [config, reloadTrigger]);
    const handleSlashCommand = useCallback(async (rawQuery, oneTimeShellAllowlist, overwriteConfirmed) => {
        setIsProcessing(true);
        try {
            if (typeof rawQuery !== 'string') {
                return false;
            }
            const trimmed = rawQuery.trim();
            if (!trimmed.startsWith('/') && !trimmed.startsWith('?')) {
                return false;
            }
            const userMessageTimestamp = Date.now();
            // Sanitize the command if it contains sensitive data
            const sanitizedCommand = trimmed.startsWith('/key ')
                ? secureInputHandler.sanitizeForHistory(trimmed)
                : trimmed;
            addItem({ type: MessageType.USER, text: sanitizedCommand }, userMessageTimestamp);
            const parts = trimmed.substring(1).trim().split(/\s+/);
            const commandPath = parts.filter((p) => p); // The parts of the command, e.g., ['memory', 'add']
            let currentCommands = commands;
            let commandToExecute;
            let pathIndex = 0;
            const canonicalPath = [];
            for (const part of commandPath) {
                // TODO: For better performance and architectural clarity, this two-pass
                // search could be replaced. A more optimal approach would be to
                // pre-compute a single lookup map in `CommandService.ts` that resolves
                // all name and alias conflicts during the initial loading phase. The
                // processor would then perform a single, fast lookup on that map.
                // First pass: check for an exact match on the primary command name.
                let foundCommand = currentCommands.find((cmd) => cmd.name === part);
                // Second pass: if no primary name matches, check for an alias.
                if (!foundCommand) {
                    foundCommand = currentCommands.find((cmd) => cmd.altNames?.includes(part));
                }
                if (foundCommand) {
                    commandToExecute = foundCommand;
                    canonicalPath.push(foundCommand.name);
                    pathIndex++;
                    if (foundCommand.subCommands) {
                        currentCommands = foundCommand.subCommands;
                    }
                    else {
                        break;
                    }
                }
                else {
                    break;
                }
            }
            if (commandToExecute) {
                const args = parts.slice(pathIndex).join(' ');
                if (commandToExecute.action) {
                    if (config) {
                        const resolvedCommandPath = canonicalPath;
                        const event = new SlashCommandEvent(resolvedCommandPath[0], resolvedCommandPath.length > 1
                            ? resolvedCommandPath.slice(1).join(' ')
                            : undefined);
                        logSlashCommand(config, event);
                    }
                    const fullCommandContext = {
                        ...commandContext,
                        invocation: {
                            raw: trimmed,
                            name: commandToExecute.name,
                            args,
                        },
                        overwriteConfirmed,
                    };
                    // If a one-time list is provided for a "Proceed" action, temporarily
                    // augment the session allowlist for this single execution.
                    if (oneTimeShellAllowlist && oneTimeShellAllowlist.size > 0) {
                        fullCommandContext.session = {
                            ...fullCommandContext.session,
                            sessionShellAllowlist: new Set([
                                ...fullCommandContext.session.sessionShellAllowlist,
                                ...oneTimeShellAllowlist,
                            ]),
                        };
                    }
                    const result = await commandToExecute.action(fullCommandContext, args);
                    if (result) {
                        switch (result.type) {
                            case 'tool':
                                return {
                                    type: 'schedule_tool',
                                    toolName: result.toolName,
                                    toolArgs: result.toolArgs,
                                };
                            case 'message':
                                addItem({
                                    type: result.messageType === 'error'
                                        ? MessageType.ERROR
                                        : MessageType.INFO,
                                    text: result.content,
                                }, Date.now());
                                return { type: 'handled' };
                            case 'dialog':
                                switch (result.dialog) {
                                    case 'help': {
                                        const helpItem = {
                                            type: 'help',
                                            timestamp: new Date(),
                                        };
                                        addItem(helpItem, Date.now());
                                        return { type: 'handled' };
                                    }
                                    case 'auth':
                                        openAuthDialog();
                                        return { type: 'handled' };
                                    case 'theme':
                                        openThemeDialog();
                                        return { type: 'handled' };
                                    case 'editor':
                                        openEditorDialog();
                                        return { type: 'handled' };
                                    case 'privacy':
                                        openPrivacyNotice();
                                        return { type: 'handled' };
                                    case 'provider':
                                        openProviderDialog();
                                        return { type: 'handled' };
                                    case 'providerModel':
                                        openProviderModelDialog();
                                        return { type: 'handled' };
                                    case 'loadProfile':
                                        openLoadProfileDialog();
                                        return { type: 'handled' };
                                    case 'tools': {
                                        // Extract the subcommand from the original command
                                        const originalCommand = rawQuery.trim();
                                        const commandParts = originalCommand.split(/\s+/);
                                        const subCommand = commandParts[1];
                                        openToolsDialog(subCommand);
                                        return { type: 'handled' };
                                    }
                                    case 'logging': {
                                        // For now, show the log entries as text since dialog system is not fully implemented
                                        const dialogData = result.dialogData;
                                        if (dialogData && dialogData.entries) {
                                            // This would open the LoggingDialog when the dialog system is ready
                                            // For now, the command handles displaying the logs
                                        }
                                        return { type: 'handled' };
                                    }
                                    case 'settings':
                                        openSettingsDialog();
                                        return { type: 'handled' };
                                    default: {
                                        const unhandled = result.dialog;
                                        throw new Error(`Unhandled slash command result: ${unhandled}`);
                                    }
                                }
                            case 'load_history': {
                                // Only clear UI history when loading a saved chat checkpoint (e.g., /chat resume)
                                // Do NOT clear when switching providers or loading profiles - they preserve conversation
                                // The load_history action is only returned by /chat resume command
                                fullCommandContext.ui.clear();
                                result.history.forEach((item, index) => {
                                    fullCommandContext.ui.addItem(item, index);
                                });
                                // Set the client history - it will be stored for later use if not initialized
                                const client = config?.getGeminiClient();
                                if (client) {
                                    await client.setHistory(result.clientHistory, {
                                        stripThoughts: true,
                                    });
                                }
                                return { type: 'handled' };
                            }
                            case 'quit':
                                setQuittingMessages(result.messages);
                                setTimeout(() => {
                                    runExitCleanup();
                                    process.exit(0);
                                }, 100);
                                return { type: 'handled' };
                            case 'submit_prompt':
                                return {
                                    type: 'submit_prompt',
                                    content: result.content,
                                };
                            case 'confirm_shell_commands': {
                                const { outcome, approvedCommands } = await new Promise((resolve) => {
                                    setShellConfirmationRequest({
                                        commands: result.commandsToConfirm,
                                        onConfirm: (resolvedOutcome, resolvedApprovedCommands) => {
                                            setShellConfirmationRequest(null); // Close the dialog
                                            resolve({
                                                outcome: resolvedOutcome,
                                                approvedCommands: resolvedApprovedCommands,
                                            });
                                        },
                                    });
                                });
                                if (outcome === ToolConfirmationOutcome.Cancel ||
                                    !approvedCommands ||
                                    approvedCommands.length === 0) {
                                    return { type: 'handled' };
                                }
                                if (outcome === ToolConfirmationOutcome.ProceedAlways) {
                                    setSessionShellAllowlist((prev) => new Set([...prev, ...approvedCommands]));
                                }
                                return await handleSlashCommand(result.originalInvocation.raw, 
                                // Pass the approved commands as a one-time grant for this execution.
                                new Set(approvedCommands));
                            }
                            case 'confirm_action': {
                                const { confirmed } = await new Promise((resolve) => {
                                    setConfirmationRequest({
                                        prompt: result.prompt,
                                        onConfirm: (resolvedConfirmed) => {
                                            setConfirmationRequest(null);
                                            resolve({ confirmed: resolvedConfirmed });
                                        },
                                    });
                                });
                                if (!confirmed) {
                                    addItem({
                                        type: MessageType.INFO,
                                        text: 'Operation cancelled.',
                                    }, Date.now());
                                    return { type: 'handled' };
                                }
                                return await handleSlashCommand(result.originalInvocation.raw, undefined, true);
                            }
                            default: {
                                const unhandled = result;
                                throw new Error(`Unhandled slash command result: ${unhandled}`);
                            }
                        }
                    }
                    return { type: 'handled' };
                }
                else if (commandToExecute.subCommands) {
                    const helpText = `Command '/${commandToExecute.name}' requires a subcommand. Available:\n${commandToExecute.subCommands
                        .map((sc) => `  - ${sc.name}: ${sc.description || ''}`)
                        .join('\n')}`;
                    addMessage({
                        type: MessageType.INFO,
                        content: helpText,
                        timestamp: new Date(),
                    });
                    return { type: 'handled' };
                }
            }
            addMessage({
                type: MessageType.ERROR,
                content: `Unknown command: ${trimmed}`,
                timestamp: new Date(),
            });
            return { type: 'handled' };
        }
        catch (e) {
            addItem({
                type: MessageType.ERROR,
                text: e instanceof Error ? e.message : String(e),
            }, Date.now());
            return { type: 'handled' };
        }
        finally {
            setIsProcessing(false);
        }
    }, [
        config,
        addItem,
        openAuthDialog,
        commands,
        commandContext,
        addMessage,
        openThemeDialog,
        openPrivacyNotice,
        openEditorDialog,
        openProviderDialog,
        openProviderModelDialog,
        openLoadProfileDialog,
        openToolsDialog,
        setQuittingMessages,
        openSettingsDialog,
        setShellConfirmationRequest,
        setSessionShellAllowlist,
        setIsProcessing,
        setConfirmationRequest,
    ]);
    return {
        handleSlashCommand,
        slashCommands: commands,
        pendingHistoryItems,
        commandContext,
        shellConfirmationRequest,
        confirmationRequest,
    };
};
//# sourceMappingURL=slashCommandProcessor.js.map