/**
 * Migrate Worker Command
 * Run worker package migrations
 */
import { BaseCommand } from '../BaseCommand.js';
import { confirmProductionRun, mapConnectionToOrmConfig, parseRollbackSteps, } from '../utils/DatabaseCliUtils.js';
import { databaseConfig } from '../../config/database.js';
import { Migrator } from '../../migrations/Migrator.js';
import * as path from '../../node-singletons/path.js';
import { Database } from '../../orm/Database.js';
import { DatabaseAdapterRegistry } from '../../orm/DatabaseAdapterRegistry.js';
const isBuiltInDriver = (driver) => driver === 'sqlite' ||
    driver === 'mysql' ||
    driver === 'postgresql' ||
    driver === 'sqlserver' ||
    driver === 'd1' ||
    driver === 'd1-remote';
const getWorkerPersistenceConnectionName = (optionValue) => {
    if (typeof optionValue === 'string') {
        const trimmed = optionValue.trim();
        if (trimmed.length > 0)
            return trimmed;
    }
    if (typeof process === 'undefined')
        return 'default';
    const raw = process.env?.['WORKER_PERSISTENCE_DB_CONNECTION'];
    const value = typeof raw === 'string' ? raw.trim() : '';
    return value.length > 0 ? value : 'default';
};
const addOptions = (command) => {
    command
        .option('--status', 'Display migration status (applied, pending, failed)')
        .option('--fresh', 'Reset database: drop all tables and re-run all migrations')
        .option('--reset', 'Rollback all migrations to initial state')
        .option('--rollback', 'Rollback last migration batch')
        .option('--step <number>', 'Number of batches to rollback (use with --rollback)', '1')
        .option('--force', 'Skip production confirmation (allow unsafe operations in production)')
        .option('--all', 'Run migrations for all configured database connections')
        .option('--connection <name>', 'Use a specific database connection for worker migrations')
        .option('--no-interactive', 'Disable interactive prompts (useful for CI/CD)');
};
const getInteractive = (options) => options['interactive'] !== false;
const isDestructiveAction = (options) => options['fresh'] === true || options['reset'] === true || options['rollback'] === true;
const printStatus = async (migrator, cmd) => {
    const rows = await migrator.status();
    if (rows.length === 0) {
        cmd.info('No migrations found.');
        return;
    }
    for (const row of rows) {
        const tag = row.status ?? (row.applied ? 'applied' : 'pending');
        const extra = row.applied ? ` (batch=${row.batch ?? '?'}, at=${row.appliedAt ?? '?'})` : '';
        cmd.info(`${tag}: ${row.name}${extra}`);
    }
};
const applyMigrations = async (migrator, cmd) => {
    const result = await migrator.migrate();
    if (result.appliedNames.length === 0) {
        cmd.info('No pending worker migrations.');
        return;
    }
    cmd.success('Worker migrations applied.');
    for (const name of result.appliedNames) {
        cmd.info(`✓ ${name}`);
    }
};
const runActions = async (migrator, options, cmd, driver) => {
    if (options['status'] === true) {
        cmd.info(`Adapter: ${driver}`);
        await printStatus(migrator, cmd);
        return;
    }
    if (options['fresh'] === true) {
        await migrator.fresh();
        cmd.success('Worker migrations applied (fresh).');
        return;
    }
    if (options['reset'] === true) {
        await migrator.resetAll();
        cmd.success('Worker migrations reset.');
        return;
    }
    if (options['rollback'] === true) {
        const steps = parseRollbackSteps(options);
        const result = await migrator.rollbackLastBatch(steps);
        cmd.success(`Worker migrations rolled back (${result.rolledBack}).`);
        return;
    }
    await applyMigrations(migrator, cmd);
};
const runForConnection = async (conn, options, cmd, interactive) => {
    const destructive = isDestructiveAction(options);
    const proceed = await confirmProductionRun({
        cmd,
        interactive,
        destructive,
        force: options['force'] === true,
        message: 'NODE_ENV=production. Continue running worker migrations?',
    });
    if (!proceed)
        return;
    if (!isBuiltInDriver(conn.driver) && !DatabaseAdapterRegistry.has(conn.driver)) {
        cmd.warn(`Missing adapter for driver: ${conn.driver}`);
        cmd.warn(`Install via 'zin plugin install adapter:${conn.driver}' (or 'zin add db:${conn.driver}').`);
    }
    const previousD1RemoteMode = typeof process === 'undefined' ? undefined : process.env['D1_REMOTE_MODE'];
    if (conn.driver === 'd1-remote' && typeof process !== 'undefined') {
        // Important: set BEFORE Database.create() so D1RemoteAdapter is constructed in SQL mode.
        // Registry mode uses /zin/d1/statement, which is not appropriate for migrations.
        process.env['D1_REMOTE_MODE'] = 'sql';
    }
    const ormConfig = mapConnectionToOrmConfig(conn);
    const db = Database.create(ormConfig);
    await db.connect();
    try {
        const migrator = Migrator.create({
            db,
            projectRoot: process.cwd(),
            globalDir: path.join(process.cwd(), 'packages', 'workers', 'migrations'),
            extension: databaseConfig.migrations.extension,
            separateTracking: true,
        });
        await runActions(migrator, options, cmd, conn.driver);
    }
    finally {
        if (conn.driver === 'd1-remote' && typeof process !== 'undefined') {
            if (previousD1RemoteMode === undefined) {
                delete process.env['D1_REMOTE_MODE'];
            }
            else {
                process.env['D1_REMOTE_MODE'] = previousD1RemoteMode;
            }
        }
        await db.disconnect();
    }
};
const executeMigrateWorker = async (options, cmd) => {
    const interactive = getInteractive(options);
    const targets = [];
    if (options['all'] === true) {
        for (const [name, config] of Object.entries(databaseConfig.connections)) {
            targets.push({ name, config });
        }
    }
    else {
        const selected = getWorkerPersistenceConnectionName(options['connection']);
        const hasSelected = selected.length > 0;
        const connections = databaseConfig.connections;
        const configured = hasSelected ? connections[selected] : undefined;
        targets.push({
            name: hasSelected ? selected : 'default',
            config: configured ?? databaseConfig.getConnection(),
        });
    }
    let sequence = Promise.resolve();
    for (const { name, config } of targets) {
        sequence = sequence.then(async () => {
            if (targets.length > 1) {
                cmd.info(`\n--- Connection: ${name} (${config.driver}) ---`);
            }
            await runForConnection(config, options, cmd, interactive);
        });
    }
    await sequence;
};
export const MigrateWorkerCommand = Object.freeze({
    create() {
        const cmd = BaseCommand.create({
            name: 'migrate:worker',
            description: 'Run worker package migrations',
            addOptions,
            execute: async (options) => executeMigrateWorker(options, cmd),
        });
        return cmd;
    },
});
