import { loadCustomSlashCommands } from "../utils/customCommands.js";
import { substituteCommandParameters, parseSlashCommandInput, hasParameterPlaceholders, } from "../utils/commandArgumentParser.js";
import { parseBashCommands, replaceBashCommandsWithOutput, executeBashCommands, } from "../utils/markdownParser.js";
import { countToolBlocks, formatToolTokenSummary, } from "../utils/messageOperations.js";
import { logger } from "../utils/globalLogger.js";
export class SlashCommandManager {
    constructor(container, options) {
        this.container = container;
        this.commands = new Map();
        this.customCommands = new Map();
        this.skillCommandIds = new Set();
        this.currentCommandAbortController = null;
        this.workdir = options.workdir;
    }
    initialize() {
        this.initializeBuiltinCommands();
        this.loadCustomCommands();
        // Listen for skill refreshes and update skill commands
        const skillManager = this.container.get("SkillManager");
        if (skillManager) {
            skillManager.on("refreshed", (skills) => {
                this.registerSkillCommands(skills);
            });
        }
    }
    get messageManager() {
        return this.container.get("MessageManager");
    }
    get aiManager() {
        return this.container.get("AIManager");
    }
    get backgroundTaskManager() {
        return this.container.get("BackgroundTaskManager");
    }
    get taskManager() {
        return this.container.get("TaskManager");
    }
    get skillManager() {
        return this.container.get("SkillManager");
    }
    get subagentManager() {
        return this.container.get("SubagentManager");
    }
    initializeBuiltinCommands() {
        // Register built-in clear command
        this.registerCommand({
            id: "clear",
            name: "clear",
            description: "Clear conversation history and reset session",
            handler: async () => {
                this.aiManager.abortAIMessage();
                this.messageManager.clearMessages();
                await this.taskManager.syncWithSession();
            },
        });
    }
    /**
     * Load custom commands from filesystem
     */
    loadCustomCommands() {
        try {
            const customCommands = loadCustomSlashCommands(this.workdir);
            for (const command of customCommands) {
                this.customCommands.set(command.id, command);
                // Generate description: prioritize custom description, otherwise use default description
                const description = command.description ||
                    `Custom command: ${command.name}${hasParameterPlaceholders(command.content) ? " (supports parameters)" : ""}`;
                // Register as a regular command with a handler that executes the custom command
                this.registerCommand({
                    id: command.id,
                    name: command.name,
                    description,
                    handler: async (args) => {
                        // Substitute parameters in the command content
                        let processedContent = command.content;
                        if (args) {
                            processedContent = substituteCommandParameters(processedContent, args);
                        }
                        await this.executeCustomCommandInMainAgent(command.name, processedContent, command.config);
                    },
                });
            }
            logger?.debug(`Loaded ${customCommands.length} custom commands`);
        }
        catch (error) {
            logger?.warn("Failed to load custom commands:", error);
        }
    }
    /**
     * Register skills as slash commands
     */
    registerSkillCommands(skills) {
        // Clear existing skill commands
        for (const commandId of this.skillCommandIds) {
            this.unregisterCommand(commandId);
        }
        this.skillCommandIds.clear();
        for (const skill of skills) {
            if (skill.userInvocable === false) {
                continue;
            }
            const commandId = skill.name;
            this.skillCommandIds.add(commandId);
            this.registerCommand({
                id: commandId,
                name: skill.name,
                description: `Skill: ${skill.description}`,
                handler: async (args, signal) => {
                    try {
                        // 1. Prepare skill content immediately
                        const prepared = await this.skillManager.prepareSkill({
                            skill_name: skill.name,
                            args,
                        });
                        if (!prepared.skill) {
                            // If skill not found or invalid, add error
                            this.messageManager.addErrorBlock(prepared.content);
                            return;
                        }
                        if (skill.context === "fork") {
                            // Forked skill execution: add user message with text + tool block
                            const messageId = this.messageManager.addUserMessage({
                                content: `/${skill.name}${args ? ` ${args}` : ""}`,
                                customCommandContent: prepared.content,
                            });
                            const toolBlockId = this.messageManager.addToolBlockToMessage(messageId, {
                                name: skill.name,
                                parameters: prepared.content,
                                stage: "running",
                            });
                            // Forked skill execution
                            const subagentConfigs = await this.subagentManager.loadConfigurations();
                            const subagentType = skill.agent || "general-purpose";
                            const config = subagentConfigs.find((c) => c.name === subagentType);
                            if (!config) {
                                throw new Error(`Subagent configuration for ${subagentType} not found`);
                            }
                            try {
                                const instance = await this.subagentManager.createInstance(config, {
                                    description: skill.description,
                                    prompt: prepared.content,
                                    subagent_type: subagentType,
                                    model: skill.model,
                                }, false, () => {
                                    // Update the tool block with progress
                                    const subagent = this.subagentManager.getInstance(instance.subagentId);
                                    if (subagent) {
                                        const messages = subagent.messages;
                                        const tokens = subagent.messageManager.getLatestTotalTokens();
                                        const lastTools = subagent.lastTools;
                                        const toolCount = countToolBlocks(messages);
                                        const summary = formatToolTokenSummary(toolCount, tokens);
                                        let shortResult = "";
                                        if (toolCount > 2) {
                                            shortResult += "... ";
                                        }
                                        if (lastTools.length > 0) {
                                            shortResult += `${lastTools.join(", ")} `;
                                        }
                                        shortResult += summary;
                                        this.messageManager.updateToolBlock({
                                            id: toolBlockId,
                                            messageId,
                                            shortResult,
                                        });
                                    }
                                });
                                try {
                                    const result = await this.subagentManager.executeAgent(instance, prepared.content, signal);
                                    // Update the ToolBlock with final result
                                    this.messageManager.updateToolBlock({
                                        id: toolBlockId,
                                        messageId,
                                        result,
                                        stage: "end",
                                    });
                                }
                                finally {
                                    this.subagentManager.cleanupInstance(instance.subagentId);
                                }
                            }
                            catch (error) {
                                // Update the ToolBlock with error
                                this.messageManager.updateToolBlock({
                                    id: toolBlockId,
                                    messageId,
                                    stage: "end",
                                    error: error instanceof Error ? error.message : String(error),
                                });
                                throw error; // Re-throw to be caught by outer catch for logging/error block
                            }
                            return;
                        }
                        // Non-forked skill: execute and trigger AI response
                        const result = await this.skillManager.executeSkill({
                            skill_name: skill.name,
                            args,
                        });
                        // Add user message with the processed content
                        this.messageManager.addUserMessage({
                            content: `/${skill.name}${args ? ` ${args}` : ""}`,
                            customCommandContent: result.content,
                        });
                        // Trigger AI response
                        await this.aiManager.sendAIMessage({
                            model: skill.model,
                            allowedRules: result.allowedTools,
                        });
                    }
                    catch (error) {
                        logger?.error(`Failed to execute skill command '${skill.name}':`, error);
                        this.messageManager.addErrorBlock(`Failed to execute skill command '${skill.name}': ${error instanceof Error ? error.message : String(error)}`);
                    }
                },
            });
        }
        logger?.debug(`Registered ${skills.length} skill commands`);
    }
    /**
     * Register commands from a plugin with namespacing
     */
    registerPluginCommands(pluginName, commands) {
        for (const command of commands) {
            const namespacedId = `${pluginName}:${command.id}`;
            const namespacedName = `${pluginName}:${command.name}`;
            this.customCommands.set(namespacedId, command);
            // Generate description: prioritize custom description, otherwise use default description
            const description = command.description ||
                `Plugin command: ${namespacedName}${hasParameterPlaceholders(command.content) ? " (supports parameters)" : ""}`;
            this.registerCommand({
                id: namespacedId,
                name: namespacedName,
                description,
                handler: async (args) => {
                    // Substitute parameters in the command content
                    let processedContent = command.content;
                    if (args) {
                        processedContent = substituteCommandParameters(processedContent, args);
                    }
                    await this.executeCustomCommandInMainAgent(namespacedName, processedContent, command.config, args);
                },
            });
        }
        logger?.debug(`Registered ${commands.length} commands from plugin '${pluginName}'`);
    }
    /**
     * Reload custom commands (useful for development)
     */
    reloadCustomCommands() {
        // Clear existing custom commands
        for (const commandId of this.customCommands.keys()) {
            this.unregisterCommand(commandId);
        }
        this.customCommands.clear();
        // Reload
        this.loadCustomCommands();
    }
    /**
     * Register new command
     */
    registerCommand(command) {
        this.commands.set(command.id, command);
    }
    /**
     * Unregister command
     */
    unregisterCommand(commandId) {
        return this.commands.delete(commandId);
    }
    /**
     * Get all available commands
     */
    getCommands() {
        return Array.from(this.commands.values());
    }
    /**
     * Get command by ID
     */
    getCommand(commandId) {
        return this.commands.get(commandId);
    }
    /**
     * Execute command
     */
    async executeCommand(commandId, args) {
        const command = this.commands.get(commandId);
        if (!command) {
            return false;
        }
        // Abort any previous command if it's still running
        this.currentCommandAbortController?.abort();
        this.currentCommandAbortController = new AbortController();
        try {
            await command.handler(args, this.currentCommandAbortController.signal);
            return true;
        }
        catch (error) {
            if (error instanceof Error && error.name === "AbortError") {
                logger?.debug(`Slash command ${commandId} was aborted`);
            }
            else {
                console.error(`Failed to execute slash command ${commandId}:`, error);
            }
            return false;
        }
        finally {
            this.currentCommandAbortController = null;
            // FR-013: Ensure slash commands are persisted to the session file
            await this.messageManager.saveSession();
        }
    }
    /**
     * Parse and validate a slash command input
     * Returns whether the command is valid along with parsed commandId and args
     */
    parseAndValidateSlashCommand(input) {
        try {
            const { command: commandId, args } = parseSlashCommandInput(input);
            const isValid = this.hasCommand(commandId);
            return {
                isValid,
                commandId: isValid ? commandId : undefined,
                args: isValid ? args || undefined : undefined, // Convert empty string to undefined
            };
        }
        catch (error) {
            console.error(`Failed to parse slash command input "${input}":`, error);
            return { isValid: false };
        }
    }
    /**
     * Check if command exists
     */
    hasCommand(commandId) {
        return this.commands.has(commandId);
    }
    /**
     * Get custom command details
     */
    getCustomCommand(commandId) {
        return this.customCommands.get(commandId);
    }
    /**
     * Get all custom commands
     */
    getCustomCommands() {
        return Array.from(this.customCommands.values());
    }
    /**
     * Execute custom command in main agent instead of sub-agent
     */
    async executeCustomCommandInMainAgent(commandName, content, config, args) {
        try {
            // Parse bash commands from the content
            const { commands, processedContent } = parseBashCommands(content);
            // Add user message immediately so text block shows before bash execution
            const messageId = this.messageManager.addUserMessage({
                content: `/${commandName}${args ? ` ${args}` : ""}`,
                customCommandContent: processedContent,
            });
            // Execute bash commands and update the message if any exist
            if (commands.length > 0) {
                const bashResults = await executeBashCommands(commands, this.workdir);
                const finalContent = replaceBashCommandsWithOutput(processedContent, bashResults);
                // Update the user message with the bash-processed content
                this.messageManager.updateUserMessage(messageId, {
                    customCommandContent: finalContent,
                });
            }
            // Execute the AI conversation with custom configuration
            await this.aiManager.sendAIMessage({
                model: config?.model,
                allowedRules: config?.allowedTools,
            });
        }
        catch (error) {
            logger?.error(`Failed to execute custom command '${commandName}':`, error);
            // Add error to message manager
            this.messageManager.addErrorBlock(`Failed to execute custom command '${commandName}': ${error instanceof Error ? error.message : String(error)}`);
        }
    }
    /**
     * Interrupt the currently executing slash command
     */
    abortCurrentCommand() {
        // Abort the AI manager if it's running
        this.aiManager.abortAIMessage();
        // Abort the current slash command handler
        this.currentCommandAbortController?.abort();
        this.currentCommandAbortController = null;
    }
}
