/**
 * /mcp slash command handler.
 *
 * The config files (mcp.json) are the single source of truth.
 * Each loop iteration re-reads them so the menu always reflects
 * the current state — no stale in-memory copies.
 *
 * Runtime connection status from McpManager is overlaid as
 * read-only decoration (tool counts, error messages).
 */
import chalk from 'chalk';
import { existsSync, readFileSync, writeFileSync } from 'fs';
import { join } from 'path';
import { installHostedMcpServer } from '../../app/mcp/install.js';
import { getAgentDir } from '../../config.js';
import { formatOpenError, openInEditor } from '../../shared/platform/open.js';
import { handleMcpSettingsCommand } from './mcp-settings-command.js';
const GLOBAL_CONFIG_FILENAME = 'mcp.json';
/** Return the absolute path of the global mcp.json file under the agent dir. */
function getGlobalConfigPath(globalConfigDir = getAgentDir()) {
    return join(globalConfigDir, GLOBAL_CONFIG_FILENAME);
}
// =============================================================================
// Config file helpers (source of truth)
// =============================================================================
/** Read server names from a config file. Returns [] if missing or invalid. */
function listServersInConfig(path) {
    if (!existsSync(path))
        return [];
    try {
        const parsed = JSON.parse(readFileSync(path, 'utf-8'));
        if (parsed?.mcpServers && typeof parsed.mcpServers === 'object') {
            return Object.keys(parsed.mcpServers);
        }
    }
    catch {
        /* ignore */
    }
    return [];
}
/** Remove a server entry from a config file. Returns true if removed. */
function removeServerFromConfig(path, name) {
    try {
        const raw = readFileSync(path, 'utf-8');
        const parsed = JSON.parse(raw);
        if (!parsed?.mcpServers?.[name])
            return false;
        delete parsed.mcpServers[name];
        writeFileSync(path, JSON.stringify(parsed, null, 2) + '\n', 'utf-8');
        return true;
    }
    catch {
        return false;
    }
}
/** Read the server names shown in the MCP menu. */
function getConfiguredServerNames(globalPath) {
    return listServersInConfig(globalPath);
}
/** True if a hosted MCP server failed to connect because OAuth login is required. */
function isHostedAuthFailure(status) {
    return (status.transport === 'streamable_http' &&
        status.authStatus === 'not_logged_in' &&
        status.failureKind === 'auth_required');
}
/** Render the connection-state label suffix shown next to each MCP server in the menu. */
function formatServerMenuLabel(status) {
    if (!status)
        return '(not connected)';
    if (status.status === 'connected') {
        if (status.transport === 'stdio' || status.authStatus === 'unsupported') {
            return `(${status.toolCount} tools)`;
        }
        return `(${status.toolCount} tools, ${status.authStatus.replaceAll('_', ' ')})`;
    }
    if (isHostedAuthFailure(status)) {
        return '(login required)';
    }
    if (status.transport === 'stdio' || status.authStatus === 'unsupported') {
        return '(failed)';
    }
    return `(failed, ${status.authStatus.replaceAll('_', ' ')})`;
}
/** Build a top-level MCP menu item for one server with its current status decoration. */
function buildServerMenuItem(name, status) {
    return {
        value: `server:${name}`,
        label: `${name} ${formatServerMenuLabel(status)}`,
        ...(status?.error ? { description: status.error } : {})
    };
}
/** Build a delete-server submenu item. */
function buildDeleteServerMenuItem(name) {
    return {
        value: `delete:${name}`,
        label: name
    };
}
// =============================================================================
// Main command
// =============================================================================
/** Top-level interactive `/mcp` command flow: list servers, manage, and edit configs. */
export async function handleMcpCommand(showStatus, select, input, getMcpManager, settingsManager, onLogin, onLogout, onMcpSettingsChanged, installDeps) {
    const initialMcpManager = getMcpManager();
    const globalConfigDir = initialMcpManager?.globalConfigDir ?? getAgentDir();
    const globalPath = getGlobalConfigPath(globalConfigDir);
    // No MCP configured at all.
    if (!initialMcpManager) {
        const hasGlobalConfig = existsSync(globalPath);
        const hint = hasGlobalConfig
            ? `Config found at ${globalPath} but no servers connected.`
            : `No MCP config found.\n\nCreate ${globalPath} with your server configs.\nSee: github.com/modelcontextprotocol/servers`;
        showStatus(hint);
        const choice = await select('MCP — Not configured', [
            'Add hosted MCP',
            'Open MCP config folder',
            'Cancel'
        ]);
        if (choice === 'Add hosted MCP') {
            await handleAddHostedServer(showStatus, input, getMcpManager, globalConfigDir, globalPath, onLogin, installDeps);
            return true;
        }
        if (choice === 'Open MCP config folder') {
            void openInEditor(getAgentDir()).catch((error) => showStatus(`Could not open config folder: ${formatOpenError(error)}`));
        }
        return true;
    }
    // Loop: re-read config each iteration so deletes are immediately visible.
    while (true) {
        const runtimeStatus = new Map();
        for (const server of getMcpManager()?.servers ?? []) {
            runtimeStatus.set(server.name, server);
        }
        const serverNames = getConfiguredServerNames(globalPath);
        const menuOptions = [];
        for (const name of serverNames) {
            const status = runtimeStatus.get(name);
            menuOptions.push(buildServerMenuItem(name, status));
        }
        menuOptions.push({
            value: 'mcp-settings',
            label: 'MCP settings',
            description: 'Configure hosted MCP OAuth behavior'
        });
        menuOptions.push({
            value: 'add-hosted-server',
            label: 'Add hosted MCP',
            description: 'Add a streamable HTTP MCP URL and login if required'
        });
        menuOptions.push({
            value: 'open-config',
            label: 'Open MCP config'
        });
        if (serverNames.length > 0) {
            menuOptions.push({
                value: 'delete-server',
                label: 'Delete server'
            });
        }
        menuOptions.push({
            value: 'cancel',
            label: 'Cancel'
        });
        const choice = await select('MCP Servers', menuOptions);
        if (!choice || choice === 'cancel')
            return true;
        if (choice === 'open-config') {
            void openInEditor(globalPath).catch((error) => showStatus(`Could not open ${globalPath}: ${formatOpenError(error)}`));
            showStatus(`Opened ${globalPath} — restart session after editing`);
            return true;
        }
        if (choice === 'delete-server') {
            await handleDeleteServer(showStatus, select, globalPath);
            continue; // Re-read config and show updated menu.
        }
        if (choice === 'mcp-settings') {
            await handleMcpSettingsCommand(showStatus, select, input, settingsManager, onMcpSettingsChanged);
            continue;
        }
        if (choice === 'add-hosted-server') {
            await handleAddHostedServer(showStatus, input, getMcpManager, globalConfigDir, globalPath, onLogin, installDeps);
            continue;
        }
        // User selected a server — show its tools, then loop back.
        if (!choice.startsWith('server:')) {
            continue;
        }
        const serverName = choice.slice('server:'.length);
        const status = runtimeStatus.get(serverName);
        if (!status) {
            showStatus(`${serverName}: in config but not connected this session. Restart to connect.`);
        }
        else if (isHostedAuthFailure(status)) {
            await handleHostedLoginRequiredServer(select, showStatus, status, globalPath, onLogin);
        }
        else if (status.status === 'connected' &&
            status.transport === 'streamable_http' &&
            status.authStatus === 'oauth') {
            await handleHostedConnectedServer(select, showStatus, status, globalPath, onLogout);
        }
        else if (status.status === 'failed') {
            showStatus(chalk.red(`${status.name}: failed to connect`) +
                `\n\n${status.error ?? 'Unknown error'}` +
                '\n\nCheck the command and args in your mcp.json config.');
        }
        else {
            const toolLines = status.toolNames.map((t) => `  ${t}`).join('\n');
            showStatus(`${status.name} — ${status.toolCount} tools:\n${toolLines}`);
        }
    }
}
async function handleAddHostedServer(showStatus, input, getMcpManager, globalConfigDir, globalPath, onLogin, installDeps) {
    if (!installDeps) {
        showStatus(`Open ${globalPath} to add hosted MCP servers.`);
        return;
    }
    const name = (await input('MCP server name', 'epic'))?.trim();
    if (!name)
        return;
    const url = (await input('Hosted MCP URL', 'https://host/path/mcp'))?.trim();
    if (!url)
        return;
    try {
        const result = await installHostedMcpServer({
            name,
            url,
            globalConfigDir,
            authStorage: installDeps.authStorage,
            mcpOAuthStore: installDeps.mcpOAuthStore
        });
        showStatus(`Added ${name} to ${result.configPath}`);
        await installDeps.onMcpConfigChanged?.();
        const liveStatus = getMcpManager()?.servers.find((server) => server.name === name);
        const providerId = liveStatus?.oauthProviderId ?? result.providerId;
        const authStatus = liveStatus?.authStatus ?? result.authStatus;
        if (authStatus === 'not_logged_in' && providerId && onLogin) {
            showStatus(`${name} requires login. Opening browser authentication...`);
            await onLogin(providerId);
            await installDeps.onMcpConfigChanged?.();
            return;
        }
        if (authStatus === 'oauth') {
            showStatus(`${name} is already logged in.`);
            return;
        }
        if (authStatus === 'unsupported') {
            showStatus(`${name} added. No OAuth login was detected.`);
        }
    }
    catch (error) {
        showStatus(chalk.red(error instanceof Error ? error.message : String(error)));
    }
}
/** Submenu for a connected hosted MCP server: logout or open config. */
async function handleHostedConnectedServer(select, showStatus, status, globalPath, onLogout) {
    while (true) {
        const choice = await select(`${status.name} — hosted MCP`, ['Logout', 'Open config', 'Back']);
        if (!choice || choice === 'Back') {
            return;
        }
        if (choice === 'Logout') {
            if (!onLogout) {
                showStatus(`${status.name}: hosted MCP is connected.\n\nRun /logout and choose the matching MCP provider, then retry or restart the session.`);
                return;
            }
            await onLogout(status.oauthProviderId);
            return;
        }
        if (choice === 'Open config') {
            void openInEditor(globalPath).catch((error) => showStatus(`Could not open ${globalPath}: ${formatOpenError(error)}`));
            showStatus(`Opened ${globalPath} — restart session after editing`);
            return;
        }
    }
}
/** Submenu for a hosted MCP server that requires login: trigger login or open config. */
async function handleHostedLoginRequiredServer(select, showStatus, status, globalPath, onLogin) {
    while (true) {
        const choice = await select(`${status.name} — hosted MCP`, ['Login', 'Open config', 'Back']);
        if (!choice || choice === 'Back') {
            return;
        }
        if (choice === 'Login') {
            if (!onLogin) {
                showStatus(`${status.name}: hosted MCP login required.\n\nRun /login and choose the matching MCP provider, then retry or restart the session.`);
                return;
            }
            await onLogin(status.oauthProviderId);
            return;
        }
        if (choice === 'Open config') {
            void openInEditor(globalPath).catch((error) => showStatus(`Could not open ${globalPath}: ${formatOpenError(error)}`));
            showStatus(`Opened ${globalPath} — restart session after editing`);
            return;
        }
    }
}
// =============================================================================
// Delete server
// =============================================================================
/** Interactive flow that prompts for a server to remove from config. */
async function handleDeleteServer(showStatus, select, globalPath) {
    const globalServers = listServersInConfig(globalPath);
    const options = [];
    for (const name of globalServers) {
        options.push(buildDeleteServerMenuItem(name));
    }
    if (options.length === 0) {
        showStatus('No servers found in config files.');
        return;
    }
    options.push({
        value: 'cancel',
        label: 'Cancel'
    });
    const choice = await select('Delete MCP server', options);
    if (!choice || choice === 'cancel')
        return;
    const match = /^delete:(.*)$/.exec(choice);
    if (!match)
        return;
    const [, name] = match;
    const confirm = await select(`Remove "${name}" from mcp.json? Restart session to take effect.`, [
        'Remove',
        'Cancel'
    ]);
    if (confirm !== 'Remove')
        return;
    let removed = false;
    if (globalServers.includes(name)) {
        removed = removeServerFromConfig(globalPath, name) || removed;
    }
    if (removed) {
        showStatus(`Removed "${name}" — restart session to take effect`);
    }
    else {
        showStatus(`Failed to remove "${name}" from config.`);
    }
}
//# sourceMappingURL=mcp-command.js.map