/**
 * Db Seed Command
 * Run database seeders from database/seeders
 */
import { SeederDiscovery } from '../../seeders/SeederDiscovery.js';
import { SeederLoader } from '../../seeders/SeederLoader.js';
import { BaseCommand } from '../BaseCommand.js';
import { confirmProductionRun, mapConnectionToOrmConfig } from '../utils/DatabaseCliUtils.js';
import { databaseConfig } from '../../config/database.js';
import { Env } from '../../config/env.js';
import { ErrorFactory } from '../../exceptions/ZintrustError.js';
import { resetDatabase, useDatabase } from '../../orm/Database.js';
const addSeedOptions = (command) => {
    command
        .option('--dir <path>', 'Seeders directory (relative to project root)', databaseConfig.seeders.directory)
        .option('--service <domain/name>', 'Run global + service-local seeders')
        .option('--only-service <domain/name>', 'Run only service-local seeders')
        .option('--no-interactive', 'Skip interactive prompts');
};
const getInteractive = (options) => options['interactive'] !== false && Env.CI !== 'true';
const ensureNonD1Driver = (driver) => {
    if (driver === 'd1' || driver === 'd1-remote') {
        throw ErrorFactory.createCliError('This project is configured for D1. Seeding via `zin db:seed` is not supported yet.');
    }
};
const getServiceArgs = (options) => {
    let serviceArg;
    if (typeof options['onlyService'] === 'string') {
        serviceArg = String(options['onlyService']);
    }
    else if (typeof options['service'] === 'string') {
        serviceArg = String(options['service']);
    }
    const includeGlobal = typeof options['onlyService'] !== 'string';
    return { service: serviceArg, includeGlobal };
};
const parseServiceRef = (raw) => {
    const trimmed = raw.trim();
    const parts = trimmed.split('/').filter((p) => p.trim().length > 0);
    if (parts.length !== 2) {
        throw ErrorFactory.createCliError(`Invalid service reference: '${raw}'. Expected format: <domain>/<name>`);
    }
    return { domain: parts[0], name: parts[1] };
};
const normalizeSeederNameArg = (raw) => {
    const trimmed = raw.trim();
    if (trimmed.endsWith('.ts'))
        return trimmed.slice(0, -3);
    if (trimmed.endsWith('.js'))
        return trimmed.slice(0, -3);
    return trimmed;
};
const selectSeederFiles = (files, seederName) => {
    if (seederName !== undefined) {
        return files.filter((filePath) => {
            const base = filePath.split('/').pop() ?? '';
            const withoutExt = base.replace(/\.(ts|js)$/u, '');
            return withoutExt === seederName;
        });
    }
    const databaseSeeder = files.find((filePath) => {
        const base = filePath.split('/').pop() ?? '';
        const withoutExt = base.replace(/\.(ts|js)$/u, '');
        return withoutExt === 'DatabaseSeeder';
    });
    if (databaseSeeder !== undefined)
        return [databaseSeeder];
    return files;
};
const executeSeed = async (options, cmd) => {
    const interactive = getInteractive(options);
    const okToProceed = await confirmProductionRun({
        cmd,
        interactive,
        message: 'NODE_ENV=production. Continue running seeders?',
    });
    if (!okToProceed)
        return;
    const conn = databaseConfig.getConnection();
    ensureNonD1Driver(conn.driver);
    const ormConfig = mapConnectionToOrmConfig(conn);
    const db = useDatabase(ormConfig, 'default');
    const dirOpt = typeof options['dir'] === 'string' ? options['dir'] : databaseConfig.seeders.directory;
    const { service, includeGlobal } = getServiceArgs(options);
    const projectRoot = process.cwd();
    const globalDir = SeederDiscovery.resolveDir(projectRoot, dirOpt);
    const globalFiles = includeGlobal ? SeederDiscovery.listSeederFiles(globalDir) : [];
    let serviceFiles = [];
    if (service !== undefined) {
        const { domain, name } = parseServiceRef(service);
        const serviceRoot = `${projectRoot}/services/${domain}/${name}`;
        const serviceDir = SeederDiscovery.resolveDir(serviceRoot, dirOpt);
        serviceFiles = SeederDiscovery.listSeederFiles(serviceDir);
    }
    const seederArgRaw = Array.isArray(options.args) ? options.args[0] : undefined;
    const seederName = typeof seederArgRaw === 'string' && seederArgRaw.trim() !== ''
        ? normalizeSeederNameArg(seederArgRaw)
        : undefined;
    const selectedGlobal = selectSeederFiles(globalFiles, seederName);
    const selectedService = selectSeederFiles(serviceFiles, seederName);
    const selected = [...selectedGlobal, ...selectedService];
    if (seederName !== undefined) {
        if (selected.length === 0) {
            throw ErrorFactory.createCliError(`Seeder not found: ${seederName} (dir=${dirOpt}, global=${globalFiles.length}, service=${serviceFiles.length})`);
        }
        if (selected.length > 1) {
            throw ErrorFactory.createCliError(`Seeder name is ambiguous: ${seederName} (matches=${selected.length}). Use --only-service or adjust name.`);
        }
    }
    if (selected.length === 0) {
        cmd.info('No seeders found.');
        return;
    }
    cmd.info(`Running ${selected.length} seeder(s)...`);
    await db.connect();
    try {
        await selected.reduce(async (previous, filePath) => {
            await previous;
            const loaded = await SeederLoader.load(filePath);
            cmd.info(`→ ${loaded.name}`);
            await loaded.run(db);
            cmd.success(`✓ Seeded: ${loaded.name}`);
        }, Promise.resolve());
    }
    finally {
        await resetDatabase();
    }
};
export const DbSeedCommand = Object.freeze({
    create() {
        const cmd = BaseCommand.create({
            name: 'db:seed',
            description: 'Run database seeders',
            addOptions: addSeedOptions,
            execute: async (options) => executeSeed(options, cmd),
        });
        return cmd;
    },
});
