#!/usr/bin/env node
import { Command } from 'commander';
import { setVerbose, setJsonMode, closeFileLogger } from '../lib/index.js';
import { isMcpError, formatHumanError } from '../lib/index.js';
import { formatJson, formatJsonError, rainbow } from './output.js';
import * as tools from './commands/tools.js';
import * as resources from './commands/resources.js';
import * as prompts from './commands/prompts.js';
import * as sessions from './commands/sessions.js';
import * as logging from './commands/logging.js';
import * as utilities from './commands/utilities.js';
import * as auth from './commands/auth.js';
import { clean } from './commands/clean.js';
import { findTarget, extractOptions, hasCommandAfterTarget, getVerboseFromEnv, getJsonFromEnv, validateOptions, validateCleanTypes, validateArgValues } from './parser.js';
import packageJson from '../../package.json' with { type: 'json' };
function getOptionsFromCommand(command) {
    const opts = command.optsWithGlobals ? command.optsWithGlobals() : command.opts();
    const verbose = opts.verbose || getVerboseFromEnv();
    if (verbose)
        setVerbose(true);
    const json = opts.json || getJsonFromEnv();
    if (json)
        setJsonMode(true);
    const options = {
        outputMode: (json ? 'json' : 'human'),
    };
    if (opts.config)
        options.config = opts.config;
    if (opts.header) {
        options.headers = Array.isArray(opts.header) ? opts.header : [opts.header];
    }
    if (opts.timeout)
        options.timeout = parseInt(opts.timeout, 10);
    if (opts.profile)
        options.profile = opts.profile;
    if (verbose)
        options.verbose = verbose;
    if (opts.schema)
        options.schema = opts.schema;
    if (opts.schemaMode) {
        const mode = opts.schemaMode;
        if (mode !== 'strict' && mode !== 'compatible' && mode !== 'ignore') {
            throw new Error(`Invalid schema mode: ${mode}. Must be 'strict', 'compatible', or 'ignore'.`);
        }
        options.schemaMode = mode;
    }
    if (opts.full)
        options.full = opts.full;
    return options;
}
async function main() {
    const args = process.argv.slice(2);
    const handleExit = () => {
        void closeFileLogger().then(() => {
            process.exit(0);
        });
    };
    process.on('SIGTERM', handleExit);
    process.on('SIGINT', handleExit);
    process.on('exit', () => {
        void closeFileLogger();
    });
    if (args.includes('--version') || args.includes('-v')) {
        const options = extractOptions(args);
        if (options.json) {
            setJsonMode(true);
            console.log(formatJson({ version: packageJson.version }));
        }
        else {
            console.log(packageJson.version);
        }
        return;
    }
    if (args.includes('--help') || args.includes('-h')) {
        const program = createProgram();
        await program.parseAsync(process.argv);
        return;
    }
    try {
        validateOptions(args);
        validateArgValues(args);
    }
    catch (error) {
        console.error(formatHumanError(error, false));
        process.exit(1);
    }
    const cleanArg = args.find((arg) => arg === '--clean' || arg.startsWith('--clean='));
    if (cleanArg) {
        const options = extractOptions(args);
        if (options.verbose)
            setVerbose(true);
        if (options.json)
            setJsonMode(true);
        const cleanValue = cleanArg.includes('=') ? cleanArg.split('=')[1] : '';
        const cleanTypes = cleanValue ? cleanValue.split(',').map((s) => s.trim()) : [];
        try {
            validateCleanTypes(cleanTypes);
        }
        catch (error) {
            console.error(formatHumanError(error, false));
            process.exit(1);
        }
        await clean({
            outputMode: options.json ? 'json' : 'human',
            sessions: cleanTypes.includes('sessions'),
            profiles: cleanTypes.includes('profiles'),
            logs: cleanTypes.includes('logs'),
            all: cleanTypes.includes('all'),
        });
        await closeFileLogger();
        return;
    }
    const targetInfo = findTarget(args);
    if (!targetInfo) {
        const { json } = extractOptions(args);
        if (json)
            setJsonMode(true);
        await sessions.listSessionsAndAuthProfiles({ outputMode: json ? 'json' : 'human' });
        if (!json) {
            console.log('\nRun "mcpc --help" for usage information.\n');
        }
        await closeFileLogger();
        return;
    }
    const { target, targetIndex } = targetInfo;
    const modifiedArgs = [
        ...process.argv.slice(0, 2),
        ...args.slice(0, targetIndex),
        ...args.slice(targetIndex + 1),
    ];
    try {
        await handleCommands(target, modifiedArgs);
    }
    finally {
        await closeFileLogger();
    }
    process.exit(0);
}
function createProgram() {
    const program = new Command();
    program.configureOutput({
        outputError: (str, write) => write(str),
        getOutHelpWidth: () => 100,
        getErrHelpWidth: () => 100,
    });
    program
        .name('mcpc')
        .description(`${rainbow('Universal')} command-line client for the Model Context Protocol (MCP).`)
        .usage('[options] <target> [command]')
        .helpOption('-h, --help', 'Display general help')
        .option('-j, --json', 'Output in JSON format for scripting')
        .option('-c, --config <file>', 'Path to MCP config JSON file (e.g. ".vscode/mcp.json")')
        .option('-H, --header <header>', 'HTTP header for remote MCP server (can be repeated)')
        .version(packageJson.version, '-v, --version', 'Output the version number')
        .option('--verbose', 'Enable debug logging')
        .option('--profile <name>', 'OAuth profile for the server ("default" if not provided)')
        .option('--schema <file>', 'Validate tool/prompt schema against expected schema')
        .option('--schema-mode <mode>', 'Schema validation mode: strict, compatible (default), ignore')
        .option('--timeout <seconds>', 'Request timeout in seconds (default: 300)')
        .option('--proxy <[host:]port>', 'Start proxy MCP server for session (with "connect" command)')
        .option('--proxy-bearer-token <token>', 'Require authentication for access to proxy server')
        .option('--clean[=types]', 'Clean up mcpc data (types: sessions, logs, profiles, all)');
    const docsUrl = process.stdout.isTTY
        ? `https://github.com/apify/mcpc/tree/v${packageJson.version}`
        : `https://raw.githubusercontent.com/apify/mcpc/v${packageJson.version}/README.md`;
    program.addHelpText('after', `
Targets:
  @<session>                    Named persistent session (e.g. "@apify")
  <config-entry>                Entry in MCP config file specified by --config (e.g. "fs")
  <server-url>                  Remote MCP server URL (e.g. "mcp.apify.com")

Management commands:
  login                         Create OAuth profile with credentials for remote server
  logout                        Remove OAuth profile for remote server
  connect @<session>            Connect to server and create named persistent session
  restart                       Kill and restart a session
  close                         Close a session

MCP server commands:
  help                          Show server info ("help" can be omitted)
  shell                         Open interactive shell
  tools-list [--full]           Send "tools/list" MCP request...
  tools-get <tool-name>
  tools-call <tool-name> [arg1:=val1 arg2:=val2 ... | <args-json> | <stdin]
  prompts-list
  prompts-get <prompt-name> [arg1:=val1 arg2:=val2 ... | <args-json> | <stdin]
  resources
  resources-list
  resources-read <uri>
  resources-subscribe <uri>
  resources-unsubscribe <uri>
  resources-templates-list
  logging-set-level <level>
  ping
  
Run "mcpc" (without <target>) to show available sessions and profiles.

Full docs: ${docsUrl}`);
    return program;
}
async function handleCommands(target, args) {
    const program = createProgram();
    program.argument('<target>', 'Target (session @name, MCP config entry, or server URL)');
    if (!hasCommandAfterTarget(args)) {
        const options = extractOptions(args);
        if (options.verbose)
            setVerbose(true);
        if (options.json)
            setJsonMode(true);
        await sessions.showServerDetails(target, {
            outputMode: options.json ? 'json' : 'human',
            ...(options.verbose && { verbose: true }),
            ...(options.config && { config: options.config }),
            ...(options.headers && { headers: options.headers }),
            ...(options.timeout !== undefined && { timeout: options.timeout }),
        });
        return;
    }
    program
        .command('help')
        .description('Show server instructions and available capabilities')
        .action(async (_options, command) => {
        await sessions.showHelp(target, getOptionsFromCommand(command));
    });
    program
        .command('shell')
        .description('Interactive shell for the target')
        .action(async () => {
        await sessions.openShell(target);
    });
    program
        .command('close')
        .description('Close the session')
        .action(async (_options, command) => {
        await sessions.closeSession(target, getOptionsFromCommand(command));
    });
    program
        .command('restart')
        .description('Restart the session (stop and start the bridge)')
        .action(async (_options, command) => {
        await sessions.restartSession(target, getOptionsFromCommand(command));
    });
    program
        .command('connect <name>')
        .description('Create or reconnect a named session to an MCP server')
        .action(async (name, _options, command) => {
        const opts = command.optsWithGlobals();
        await sessions.connectSession(name, target, {
            ...getOptionsFromCommand(command),
            proxy: opts.proxy,
            proxyBearerToken: opts.proxyBearerToken,
        });
    });
    program
        .command('login')
        .description('Login to a server using OAuth and save authentication profile')
        .option('--profile <name>', 'Profile name (default: default)')
        .option('--scope <scope>', 'OAuth scope(s) to request')
        .action(async (options, command) => {
        await auth.login(target, {
            profile: options.profile,
            scope: options.scope,
            ...getOptionsFromCommand(command),
        });
    });
    program
        .command('logout')
        .description('Delete an authentication profile')
        .option('--profile <name>', 'Profile name (default: default)')
        .action(async (options, command) => {
        await auth.logout(target, {
            profile: options.profile,
            ...getOptionsFromCommand(command),
        });
    });
    program
        .command('tools')
        .description('List available tools (shorthand for tools-list)')
        .option('--full', 'Show full tool details including complete input schema')
        .action(async (_options, command) => {
        await tools.listTools(target, getOptionsFromCommand(command));
    });
    program
        .command('tools-list')
        .description('List available tools')
        .option('--full', 'Show full tool details including complete input schema')
        .action(async (_options, command) => {
        await tools.listTools(target, getOptionsFromCommand(command));
    });
    program
        .command('tools-get <name>')
        .description('Get information about a specific tool')
        .action(async (name, _options, command) => {
        await tools.getTool(target, name, getOptionsFromCommand(command));
    });
    program
        .command('tools-call <name> [args...]')
        .description('Call a tool with arguments (key:=value pairs or JSON)')
        .action(async (name, args, _options, command) => {
        await tools.callTool(target, name, {
            args,
            ...getOptionsFromCommand(command),
        });
    });
    program
        .command('resources')
        .description('List available resources (shorthand for resources-list)')
        .action(async (_options, command) => {
        await resources.listResources(target, getOptionsFromCommand(command));
    });
    program
        .command('resources-list')
        .description('List available resources')
        .action(async (_options, command) => {
        await resources.listResources(target, getOptionsFromCommand(command));
    });
    program
        .command('resources-read <uri>')
        .description('Get a resource by URI')
        .option('-o, --output <file>', 'Write resource to file')
        .option('--max-size <bytes>', 'Maximum resource size in bytes')
        .action(async (uri, options, command) => {
        await resources.getResource(target, uri, {
            output: options.output,
            raw: options.raw,
            maxSize: options.maxSize,
            ...getOptionsFromCommand(command),
        });
    });
    program
        .command('resources-subscribe <uri>')
        .description('Subscribe to resource updates')
        .action(async (uri, _options, command) => {
        await resources.subscribeResource(target, uri, getOptionsFromCommand(command));
    });
    program
        .command('resources-unsubscribe <uri>')
        .description('Unsubscribe from resource updates')
        .action(async (uri, _options, command) => {
        await resources.unsubscribeResource(target, uri, getOptionsFromCommand(command));
    });
    program
        .command('resources-templates-list')
        .description('List available resource templates')
        .action(async (_options, command) => {
        await resources.listResourceTemplates(target, getOptionsFromCommand(command));
    });
    program
        .command('prompts')
        .description('List available prompts (shorthand for prompts-list)')
        .action(async (_options, command) => {
        await prompts.listPrompts(target, getOptionsFromCommand(command));
    });
    program
        .command('prompts-list')
        .description('List available prompts')
        .action(async (_options, command) => {
        await prompts.listPrompts(target, getOptionsFromCommand(command));
    });
    program
        .command('prompts-get <name> [args...]')
        .description('Get a prompt by name with arguments (key:=value pairs or JSON)')
        .action(async (name, args, _options, command) => {
        await prompts.getPrompt(target, name, {
            args,
            ...getOptionsFromCommand(command),
        });
    });
    program
        .command('logging-set-level <level>')
        .description('Set server logging level (debug, info, notice, warning, error, critical, alert, emergency)')
        .action(async (level, _options, command) => {
        await logging.setLogLevel(target, level, getOptionsFromCommand(command));
    });
    program
        .command('ping')
        .description('Ping the MCP server to check if it is alive')
        .action(async (_options, command) => {
        await utilities.ping(target, getOptionsFromCommand(command));
    });
    try {
        await program.parseAsync(args);
    }
    catch (error) {
        const opts = program.opts();
        const outputMode = opts.json ? 'json' : 'human';
        if (isMcpError(error)) {
            if (outputMode === 'json') {
                console.error(formatJsonError(error, error.code));
            }
            else {
                console.error(formatHumanError(error, opts.verbose));
            }
            process.exit(error.code);
        }
        console.error(outputMode === 'json'
            ? formatJsonError(error, 1)
            : formatHumanError(error, opts.verbose));
        process.exit(1);
    }
}
main().catch(async (error) => {
    console.error('Fatal error:', error);
    await closeFileLogger();
    process.exit(1);
});
//# sourceMappingURL=index.js.map