import { BaseCommand } from '../BaseCommand.js';
import { SpawnUtil } from '../utils/spawn.js';
import { Logger } from '../../config/logger.js';
import { ErrorFactory } from '../../exceptions/ZintrustError.js';
import { existsSync } from '../../node-singletons/fs.js';
import { join } from '../../node-singletons/path.js';
const runCompose = async (args) => {
    try {
        const exitCode = await SpawnUtil.spawnAndWait({ command: 'docker', args });
        if (exitCode !== 0)
            process.exit(exitCode);
        return;
    }
    catch (error) {
        const message = error instanceof Error ? error.message : String(error);
        if (!message.includes("'docker' not found"))
            throw error;
    }
    Logger.warn("'docker' not found. Falling back to 'docker-compose'.");
    const exitCode = await SpawnUtil.spawnAndWait({
        command: 'docker-compose',
        args: args.slice(1),
    });
    if (exitCode !== 0)
        process.exit(exitCode);
};
const deployContainerStack = async (label) => {
    const composeFile = 'docker-compose.workers.yml';
    const composePath = join(process.cwd(), composeFile);
    if (!existsSync(composePath)) {
        throw ErrorFactory.createCliError(`${composeFile} not found. Run \`zin init:cw\` first.`);
    }
    Logger.info(`Deploying ${label}...`);
    await runCompose(['compose', '-f', composePath, 'up', '-d', '--build']);
    Logger.info(`✅ ${label} deployed.`);
};
const deployProxyStack = async (label) => {
    const composeFile = 'docker-compose.proxy.yml';
    const composePath = join(process.cwd(), composeFile);
    if (!existsSync(composePath)) {
        throw ErrorFactory.createCliError(`${composeFile} not found.`);
    }
    Logger.info(`Deploying ${label}...`);
    await runCompose(['compose', '-f', composePath, 'up', '-d', '--build']);
    Logger.info(`✅ ${label} deployed.`);
};
const runDeploy = async (target, options) => {
    const normalizedTarget = target.trim().toLowerCase();
    if (normalizedTarget === 'cw' || normalizedTarget === 'cwr') {
        const label = normalizedTarget === 'cwr'
            ? 'container workers stack (cwr compatibility alias)'
            : 'container workers stack';
        await deployContainerStack(label);
        return;
    }
    if (normalizedTarget === 'cp' || normalizedTarget === 'proxy') {
        const label = normalizedTarget === 'proxy' ? 'proxy stack (proxy compatibility alias)' : 'proxy stack';
        await deployProxyStack(label);
        return;
    }
    const environment = options.env ?? target ?? 'worker';
    const config = typeof options.config === 'string' ? options.config.trim() : '';
    if (config.length > 0) {
        const full = join(process.cwd(), config);
        if (existsSync(full)) {
            // ok
        }
        else {
            throw ErrorFactory.createCliError(`Wrangler config not found: ${config}`);
        }
    }
    Logger.info(`Deploying to Cloudflare environment: ${environment}`);
    const exitCode = await SpawnUtil.spawnAndWait({
        command: 'wrangler',
        args: ['deploy', ...(config.length > 0 ? ['--config', config] : []), '--env', environment],
    });
    if (exitCode !== 0) {
        process.exit(exitCode);
    }
};
const createDeployCommand = () => {
    return BaseCommand.create({
        name: 'deploy',
        description: 'Deploy ZinTrust to Cloudflare Workers',
        addOptions: (command) => {
            command
                .argument('[target]', 'Deployment target (worker, d1-proxy, kv-proxy, production, cw, cp)', 'worker')
                .option('-e, --env <env>', 'Wrangler environment (overrides target)');
            command.option('-c, --config <path>', 'Wrangler config file (e.g. wrangler.containers-proxy.jsonc)');
        },
        execute: async (options) => {
            // Note: BaseCommand.create sets up action handler that calls execute(options).
            // However, since we define arguments '[target]', commander passes them to the action callback
            // BEFORE the options object.
            //
            // The BaseCommand.create generic action handler:
            // command.action(async (...args: unknown[]) => {
            //   const options = args.at(-2) as CommandOptions;
            //   const commandArgs = args.slice(0, -2) as string[];
            //   options.args = commandArgs;
            //   await config.execute(options);
            // });
            //
            // So 'target' will be available in options.args[0]
            const target = options.args?.[0] ?? 'worker';
            await runDeploy(target, options);
        },
    });
};
/**
 * Deploy Command Factory
 * Sealed namespace for immutability
 */
export const DeployCommand = Object.freeze({
    /**
     * Create a deploy command instance
     */
    create() {
        return createDeployCommand();
    },
});
