import chalk from 'chalk';
import { createInterface } from 'node:readline/promises';
import { AuthStorage, FileAuthStorageBackend } from '../app/auth/auth-storage.js';
import { resolveMcpAuthStatus } from '../app/mcp/auth-status.js';
import { getMcpConfigPath, loadMcpConfig, removeMcpServerConfig } from '../app/mcp/config.js';
import { getHostedMcpOAuthProviderId } from '../app/mcp/hosted-oauth-id.js';
import { registerHostedMcpOAuthProviders, unregisterHostedMcpOAuthProviders } from '../app/mcp/hosted-oauth-provider.js';
import { installHostedMcpServer } from '../app/mcp/install.js';
import { isManagedMcpOAuthProvider } from '../app/mcp/managed-oauth-providers.js';
import { McpOAuthStore } from '../app/mcp/oauth-state-store.js';
import { isHostedServerConfig } from '../app/mcp/types.js';
import { SettingsManager } from '../app/settings-manager.js';
import { getAgentDir } from '../config.js';
import { getAgentLogPath } from '../shared/logging/agent-log.js';
import { openInEditor } from '../shared/platform/open.js';
import { reportSettingsErrors } from './report-settings-errors.js';
function printMcpHelp() {
    console.log(`${chalk.bold('Usage:')}
  shortcut mcp list [--json]
  shortcut mcp get <name> [--json]
  shortcut mcp add <name> --url <url> [--no-login] [--force]
  shortcut mcp remove <name>
  shortcut mcp login <name>
  shortcut mcp logout <name>

Examples:
  shortcut mcp list
  shortcut mcp get notion --json
  shortcut mcp add notion --url https://mcp.notion.com/mcp
  shortcut mcp login notion
  shortcut mcp remove notion
`);
}
function parseMcpCommand(args) {
    if (args[0] !== 'mcp') {
        return undefined;
    }
    const [, rawSubcommand, ...rest] = args;
    if (!rawSubcommand || rawSubcommand === '-h' || rawSubcommand === '--help') {
        return { json: false, login: true, force: false, help: true };
    }
    if (!['list', 'get', 'add', 'remove', 'login', 'logout'].includes(rawSubcommand)) {
        return {
            subcommand: undefined,
            json: false,
            login: true,
            force: false,
            help: true,
            invalidOption: rawSubcommand
        };
    }
    const subcommand = rawSubcommand;
    let name;
    let url;
    let json = false;
    let login = true;
    let force = false;
    let help = false;
    let invalidOption;
    for (let i = 0; i < rest.length; i += 1) {
        const arg = rest[i];
        if (arg === '-h' || arg === '--help') {
            help = true;
            continue;
        }
        if (arg === '--json') {
            json = true;
            continue;
        }
        if (arg === '--no-login') {
            login = false;
            continue;
        }
        if (arg === '--force') {
            force = true;
            continue;
        }
        if (arg === '--url') {
            url = rest[i + 1];
            i += 1;
            continue;
        }
        if (arg.startsWith('--url=')) {
            url = arg.slice('--url='.length);
            continue;
        }
        if (arg.startsWith('-')) {
            invalidOption = invalidOption ?? arg;
            continue;
        }
        if (!name) {
            name = arg;
            continue;
        }
    }
    return { subcommand, name, url, json, login, force, help, invalidOption };
}
async function promptForOAuthCode(message, placeholder) {
    const rl = createInterface({ input: process.stdin, output: process.stdout });
    try {
        return await rl.question(`${message}${placeholder ? ` (${placeholder})` : ''}: `);
    }
    finally {
        rl.close();
    }
}
function createContext() {
    const cwd = process.cwd();
    const agentDir = getAgentDir();
    const settingsManager = SettingsManager.create(cwd, agentDir);
    reportSettingsErrors(settingsManager, 'mcp command');
    const authBackend = new FileAuthStorageBackend();
    const mcpOAuthStore = McpOAuthStore.fromStorage(authBackend, {
        settings: settingsManager.getMcpOAuthSettings(),
        isManagedMcpOAuthProvider
    });
    const authStorage = AuthStorage.fromStorage(authBackend, {
        externalCredentialStores: [mcpOAuthStore]
    });
    return {
        cwd,
        agentDir,
        configPath: getMcpConfigPath(agentDir),
        authStorage,
        mcpOAuthStore
    };
}
function redactedConfig(config) {
    if (!isHostedServerConfig(config)) {
        return config;
    }
    return {
        ...config,
        ...(config.headers
            ? { headers: Object.fromEntries(Object.keys(config.headers).map((key) => [key, '*****'])) }
            : {})
    };
}
async function getServerEntries(ctx) {
    const servers = loadMcpConfig({ cwd: ctx.cwd, globalConfigDir: ctx.agentDir });
    const entries = await Promise.all(Object.entries(servers)
        .sort(([left], [right]) => left.localeCompare(right))
        .map(async ([name, config]) => ({
        name,
        config,
        authStatus: await resolveMcpAuthStatus(name, config, {
            authStorage: ctx.authStorage,
            mcpOAuthStore: ctx.mcpOAuthStore,
            discovery: 'cached'
        })
    })));
    return entries;
}
async function runList(ctx, options) {
    const entries = await getServerEntries(ctx);
    if (options.json) {
        console.log(JSON.stringify(entries.map(({ name, config, authStatus }) => ({
            name,
            authStatus,
            config: redactedConfig(config)
        })), null, 2));
        return;
    }
    if (entries.length === 0) {
        console.log('No MCP servers configured yet. Try `shortcut mcp add notion --url https://mcp.notion.com/mcp`.');
        return;
    }
    console.log('Name\tTransport\tAuth\tTarget');
    for (const { name, config, authStatus } of entries) {
        const transport = isHostedServerConfig(config) ? 'streamable_http' : 'stdio';
        const target = isHostedServerConfig(config) ? config.url : config.command;
        console.log(`${name}\t${transport}\t${authStatus}\t${target}`);
    }
}
async function runGet(ctx, options) {
    if (!options.name) {
        throw new Error('Missing MCP server name.');
    }
    const entries = await getServerEntries(ctx);
    const entry = entries.find((candidate) => candidate.name === options.name);
    if (!entry) {
        throw new Error(`No MCP server named '${options.name}' found.`);
    }
    const providerId = isHostedServerConfig(entry.config) &&
        (entry.authStatus === 'oauth' || entry.authStatus === 'not_logged_in')
        ? getHostedMcpOAuthProviderId(entry.name, entry.config)
        : undefined;
    if (options.json) {
        console.log(JSON.stringify({
            name: entry.name,
            authStatus: entry.authStatus,
            providerId,
            config: redactedConfig(entry.config),
            configPath: ctx.configPath,
            logPath: getAgentLogPath()
        }, null, 2));
        return;
    }
    console.log(entry.name);
    console.log(`  config: ${ctx.configPath}`);
    console.log(`  logs: ${getAgentLogPath()}`);
    console.log(`  auth: ${entry.authStatus}`);
    if (providerId)
        console.log(`  provider: ${providerId}`);
    if (isHostedServerConfig(entry.config)) {
        console.log('  transport: streamable_http');
        console.log(`  url: ${entry.config.url}`);
    }
    else {
        console.log('  transport: stdio');
        console.log(`  command: ${entry.config.command}`);
        console.log(`  args: ${(entry.config.args ?? []).join(' ') || '-'}`);
        console.log(`  connection: ${entry.config.connection ?? '-'}`);
    }
    console.log(`  remove: shortcut mcp remove ${entry.name}`);
}
async function runRemove(ctx, options) {
    if (!options.name) {
        throw new Error('Missing MCP server name.');
    }
    if (!removeMcpServerConfig(ctx.configPath, options.name)) {
        console.log(`No MCP server named '${options.name}' found.`);
        return;
    }
    console.log(chalk.green(`Removed MCP server '${options.name}' from ${ctx.configPath}.`));
}
async function runLogin(ctx, options) {
    if (!options.name) {
        throw new Error('Missing MCP server name.');
    }
    const servers = loadMcpConfig({ cwd: ctx.cwd, globalConfigDir: ctx.agentDir });
    const config = servers[options.name];
    if (!config) {
        throw new Error(`No MCP server named '${options.name}' found.`);
    }
    if (!isHostedServerConfig(config)) {
        throw new Error('OAuth login is only supported for streamable HTTP MCP servers.');
    }
    const providerIds = registerHostedMcpOAuthProviders(servers, ctx.authStorage, ctx.mcpOAuthStore);
    const providerId = getHostedMcpOAuthProviderId(options.name, config);
    try {
        await ctx.authStorage.login(providerId, {
            onAuth: (info) => {
                console.log(`Open this URL to authenticate MCP: ${options.name}:`);
                console.log(info.url);
                void openInEditor(info.url).catch(() => { });
            },
            onPrompt: (prompt) => promptForOAuthCode(prompt.message, prompt.placeholder),
            onProgress: (message) => console.log(chalk.dim(message)),
            signal: new AbortController().signal
        });
    }
    finally {
        unregisterHostedMcpOAuthProviders(providerIds);
    }
    console.log(chalk.green(`Successfully logged in to MCP server '${options.name}'.`));
}
async function runLogout(ctx, options) {
    if (!options.name) {
        throw new Error('Missing MCP server name.');
    }
    const servers = loadMcpConfig({ cwd: ctx.cwd, globalConfigDir: ctx.agentDir });
    const config = servers[options.name];
    if (!config) {
        throw new Error(`No MCP server named '${options.name}' found.`);
    }
    if (!isHostedServerConfig(config)) {
        throw new Error('OAuth logout is only supported for streamable HTTP MCP servers.');
    }
    const providerId = getHostedMcpOAuthProviderId(options.name, config);
    ctx.authStorage.logout(providerId);
    ctx.mcpOAuthStore.clear(providerId);
    console.log(chalk.green(`Removed OAuth credentials for MCP server '${options.name}'.`));
}
async function runAdd(ctx, options) {
    if (!options.name || !options.url) {
        throw new Error('Missing MCP server name or URL.');
    }
    const result = await installHostedMcpServer({
        name: options.name,
        url: options.url,
        globalConfigDir: ctx.agentDir,
        authStorage: ctx.authStorage,
        mcpOAuthStore: ctx.mcpOAuthStore,
        keepProviderRegistered: true,
        force: options.force
    });
    console.log(chalk.green(`Added hosted MCP server '${options.name}' to ${result.configPath}.`));
    if (options.login && result.authStatus === 'not_logged_in' && result.providerId) {
        console.log(chalk.dim('Detected OAuth support. Starting OAuth flow...'));
        await ctx.authStorage.login(result.providerId, {
            onAuth: (info) => {
                console.log(`Open this URL to authenticate ${result.providerName ?? options.name}:`);
                console.log(info.url);
                void openInEditor(info.url).catch(() => { });
            },
            onPrompt: (prompt) => promptForOAuthCode(prompt.message, prompt.placeholder),
            onProgress: (message) => console.log(chalk.dim(message)),
            signal: new AbortController().signal
        });
        console.log(chalk.green(`Successfully logged in to MCP server '${options.name}'.`));
    }
    else if (result.authStatus === 'not_logged_in') {
        console.log(chalk.yellow(`MCP server '${options.name}' requires login. Run /login -> MCP: ${options.name} or shortcut mcp login ${options.name}.`));
    }
}
export async function handleMcpCliCommand(args) {
    const options = parseMcpCommand(args);
    if (!options) {
        return false;
    }
    if (options.help) {
        if (options.invalidOption) {
            console.error(chalk.red(`Unknown mcp subcommand '${options.invalidOption}'.`));
            process.exitCode = 1;
        }
        printMcpHelp();
        return true;
    }
    if (options.invalidOption) {
        console.error(chalk.red(`Unknown option ${options.invalidOption} for "mcp ${options.subcommand}".`));
        printMcpHelp();
        process.exitCode = 1;
        return true;
    }
    const ctx = createContext();
    try {
        switch (options.subcommand) {
            case 'list':
                await runList(ctx, options);
                break;
            case 'get':
                await runGet(ctx, options);
                break;
            case 'add':
                await runAdd(ctx, options);
                break;
            case 'remove':
                await runRemove(ctx, options);
                break;
            case 'login':
                await runLogin(ctx, options);
                break;
            case 'logout':
                await runLogout(ctx, options);
                break;
            default:
                printMcpHelp();
        }
        return true;
    }
    catch (error) {
        console.error(chalk.red(error instanceof Error ? error.message : String(error)));
        process.exitCode = 1;
        return true;
    }
}
//# sourceMappingURL=mcp-commands.js.map