"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.execute = exports.run = exports.versionAddition = exports.helpAddition = void 0;
const url_1 = require("url");
const util_1 = require("util");
const url_2 = require("url");
const config_1 = require("./config");
const help_1 = require("./help");
const settings_1 = require("./settings");
const _1 = require(".");
const path_1 = require("path");
const stream_1 = require("./cli-ux/stream");
const performance_1 = require("./performance");
const log = (message = '', ...args) => {
    message = typeof message === 'string' ? message : (0, util_1.inspect)(message);
    stream_1.stdout.write((0, util_1.format)(message, ...args) + '\n');
};
const helpAddition = (argv, config) => {
    if (argv.length === 0 && !config.pjson.oclif.default)
        return true;
    const mergedHelpFlags = (0, help_1.getHelpFlagAdditions)(config);
    for (const arg of argv) {
        if (mergedHelpFlags.includes(arg))
            return true;
        if (arg === '--')
            return false;
    }
    return false;
};
exports.helpAddition = helpAddition;
const versionAddition = (argv, config) => {
    const additionalVersionFlags = config?.pjson.oclif.additionalVersionFlags ?? [];
    const mergedVersionFlags = [...new Set(['--version', ...additionalVersionFlags]).values()];
    if (mergedVersionFlags.includes(argv[0]))
        return true;
    return false;
};
exports.versionAddition = versionAddition;
async function run(argv, options) {
    const marker = performance_1.Performance.mark('main.run');
    const initMarker = performance_1.Performance.mark('main.run#init');
    const collectPerf = async () => {
        marker?.stop();
        initMarker?.stop();
        await performance_1.Performance.collect();
        performance_1.Performance.debug();
    };
    argv = argv ?? process.argv.slice(2);
    // Handle the case when a file URL string or URL is passed in such as 'import.meta.url'; covert to file path.
    if (options && ((typeof options === 'string' && options.startsWith('file://')) || options instanceof url_2.URL)) {
        options = (0, url_1.fileURLToPath)(options);
    }
    const config = await config_1.Config.load(options ?? require.main?.filename ?? __dirname);
    let [id, ...argvSlice] = (0, help_1.normalizeArgv)(config, argv);
    // run init hook
    await config.runHook('init', { id, argv: argvSlice });
    // display version if applicable
    if ((0, exports.versionAddition)(argv, config)) {
        log(config.userAgent);
        await collectPerf();
        return;
    }
    // display help version if applicable
    if ((0, exports.helpAddition)(argv, config)) {
        const Help = await (0, help_1.loadHelpClass)(config);
        const help = new Help(config, config.pjson.helpOptions);
        await help.showHelp(argv);
        await collectPerf();
        return;
    }
    // find & run command
    const cmd = config.findCommand(id);
    if (!cmd) {
        const topic = config.flexibleTaxonomy ? null : config.findTopic(id);
        if (topic)
            return config.runCommand('help', [id]);
        if (config.pjson.oclif.default) {
            id = config.pjson.oclif.default;
            argvSlice = argv;
        }
    }
    initMarker?.stop();
    // If the the default command is '.' (signifying that the CLI is a single command CLI) and '.' is provided
    // as an argument, we need to add back the '.' to argv since it was stripped out earlier as part of the
    // command id.
    if (config.pjson.oclif.default === '.' && id === '.' && argv[0] === '.')
        argvSlice = ['.', ...argvSlice];
    try {
        return await config.runCommand(id, argvSlice, cmd);
    }
    finally {
        await collectPerf();
    }
}
exports.run = run;
function getTsConfigPath(dir, type) {
    return type === 'cjs' ? (0, path_1.join)(dir, '..', 'tsconfig.json') : (0, path_1.join)((0, path_1.dirname)((0, url_1.fileURLToPath)(dir)), '..', 'tsconfig.json');
}
/**
 * Load and run oclif CLI
 *
 * @param options - options to load the CLI
 * @returns Promise<void>
 *
 * @example For ESM dev.js
 * ```
 * #!/usr/bin/env ts-node
 * // eslint-disable-next-line node/shebang
 * (async () => {
 *   const oclif = await import('@oclif/core')
 *   await oclif.execute({type: 'esm', development: true, dir: import.meta.url})
 * })()
 * ```
 *
 * @example For ESM run.js
 * ```
 * #!/usr/bin/env node
 * // eslint-disable-next-line node/shebang
 * (async () => {
 *   const oclif = await import('@oclif/core')
 *   await oclif.execute({type: 'esm', dir: import.meta.url})
 * })()
 * ```
 *
 * @example For CJS dev.js
 * ```
 * #!/usr/bin/env node
 * // eslint-disable-next-line node/shebang
 * (async () => {
 *   const oclif = await import('@oclif/core')
 *   await oclif.execute({type: 'cjs', development: true, dir: __dirname})
 * })()
 * ```
 *
 * @example For CJS run.js
 * ```
 * #!/usr/bin/env node
 * // eslint-disable-next-line node/shebang
 * (async () => {
 *   const oclif = await import('@oclif/core')
 *   await oclif.execute({type: 'cjs', dir: import.meta.url})
 * })()
 * ```
 */
async function execute(options) {
    if (options.development) {
        // In dev mode -> use ts-node and dev plugins
        process.env.NODE_ENV = 'development';
        require('ts-node').register({
            project: getTsConfigPath(options.dir, options.type),
        });
        settings_1.settings.debug = true;
    }
    await run(options.args ?? process.argv.slice(2), options.loadOptions ?? options.dir)
        .then(async () => (0, _1.flush)())
        .catch(_1.Errors.handle);
}
exports.execute = execute;
