/**
 * Seeder Generator - Phase 6.2
 * Generates database seeders for populating development/staging databases
 * Uses factory generators for consistent data generation
 */
import { FileGenerator } from '../scaffolding/FileGenerator.js';
import { CommonUtils } from '../../common/index.js';
import { Logger } from '../../config/logger.js';
import { ErrorFactory } from '../../exceptions/ZintrustError.js';
import { fsPromises as fs } from '../../node-singletons/fs.js';
import * as path from '../../node-singletons/path.js';
/**
 * Seeder Generator - Creates database seeders using factory classes
 * Enables rapid database population for development and staging
 */
/**
 * Validate seeder options
 */
export async function validateOptions(options) {
    if (options.seederName === undefined || options.seederName.trim() === '') {
        throw ErrorFactory.createCliError('Seeder name is required');
    }
    if (!options.seederName.endsWith('Seeder')) {
        throw ErrorFactory.createCliError('Seeder name must end with "Seeder" (e.g., UserSeeder)');
    }
    if (!/^[A-Z][a-zA-Z\d]*Seeder$/.test(options.seederName)) {
        throw ErrorFactory.createCliError('Seeder name must be PascalCase ending with "Seeder"');
    }
    if (options.modelName === undefined || options.modelName.trim() === '') {
        throw ErrorFactory.createCliError('Model name is required');
    }
    if (!/^[A-Z][a-zA-Z\d]*$/.test(options.modelName)) {
        throw ErrorFactory.createCliError('Model name must be PascalCase (e.g., User, Post)');
    }
    if (options.count !== undefined && (options.count < 1 || options.count > 100000)) {
        throw ErrorFactory.createCliError('Count must be between 1 and 100000');
    }
    await assertDirectoryExists(options.seedersPath, 'Seeders path');
}
/**
 * Generate a database seeder
 */
export async function generateSeeder(options) {
    try {
        await validateOptions(options);
        const seederCode = buildSeederCode(options);
        const fileName = `${options.seederName}.ts`;
        const filePath = path.join(options.seedersPath, fileName);
        FileGenerator.writeFile(filePath, seederCode, { overwrite: true });
        Logger.info(`✅ Created seeder: ${fileName}`);
        return {
            success: true,
            filePath,
            message: `Seeder '${options.seederName}' created successfully`,
        };
    }
    catch (error) {
        Logger.error('Seeder generation failed', error);
        return {
            success: false,
            filePath: '',
            message: error.message,
        };
    }
}
async function validateDatabaseSeederOptions(options) {
    await assertDirectoryExists(options.seedersPath, 'Seeders path');
}
async function assertDirectoryExists(dirPath, label) {
    const pathStat = await fs.stat(dirPath).catch(() => null);
    if (pathStat === null) {
        throw ErrorFactory.createCliError(`${label} does not exist: ${dirPath}`);
    }
    if (!pathStat.isDirectory()) {
        throw ErrorFactory.createCliError(`${label} is not a directory: ${dirPath}`);
    }
}
export async function generateDatabaseSeeder(options) {
    try {
        await validateDatabaseSeederOptions(options);
        const seederCode = buildDatabaseSeederCode();
        const fileName = 'DatabaseSeeder.ts';
        const filePath = path.join(options.seedersPath, fileName);
        FileGenerator.writeFile(filePath, seederCode, { overwrite: true });
        Logger.info(`✅ Created seeder: ${fileName}`);
        return {
            success: true,
            filePath,
            message: `Seeder '${fileName}' created successfully`,
        };
    }
    catch (error) {
        Logger.error('DatabaseSeeder generation failed', error);
        return {
            success: false,
            filePath: '',
            message: error.message,
        };
    }
}
function buildDatabaseSeederCode() {
    return `import { SeederDiscovery } from '../../seeders/SeederDiscovery';
import { SeederLoader } from '../../seeders/SeederLoader';
import { CommonUtils } from '../../common/index';
import type { IDatabase } from '../../orm/Database';
import * as path from '../../node-singletons/path';

export const DatabaseSeeder = Object.freeze({
  async run(db: IDatabase): Promise<void> {
    const dir = CommonUtils.esmDirname(import.meta.url);
    const files = SeederDiscovery.listSeederFiles(dir).filter((filePath) => {
      const base = path.basename(filePath, path.extname(filePath));
      return base !== 'DatabaseSeeder';
    });

    for (const filePath of files) {
      const loaded = await SeederLoader.load(filePath);
      await loaded.run(db);
    }
  },
});
`;
}
/**
 * Build complete seeder code
 */
function buildSeederCode(options) {
    const imports = buildImports(options);
    const className = options.seederName;
    const count = options.count ?? 10;
    const truncate = options.truncate === false ? 'false' : 'true';
    const relationshipMethods = buildRelationshipMethods(options);
    return `/**
 * ${className}
 * Seeder for populating ${options.modelName} table with test data
 */

${imports}

export const ${className} = Object.freeze({
${buildSeederObjectBody(options, count, truncate, relationshipMethods)}
});
`;
}
/**
 * Build seeder object body
 */
function buildSeederObjectBody(options, count, truncate, relationshipMethods) {
    const factoryName = getFactoryName(options);
    const modelLower = options.modelName.toLowerCase();
    return `${buildSeederRunMethod(options, count, truncate, factoryName, modelLower)},

${buildSeederGetRecordsMethod(factoryName)},

${buildSeederWithStatesMethod(options, count, factoryName, modelLower)},

${buildSeederWithRelationshipsMethod(options, count, factoryName, modelLower, relationshipMethods)},

${buildSeederResetMethod(options)}`;
}
/**
 * Build seeder run method
 */
function buildSeederRunMethod(options, count, truncate, factoryName, modelLower) {
    const tableName = getTableName(options.modelName);
    return `  /**
   * Run the seeder
   * Populates the ${modelLower} table with ${count} records
   */
  async run(): Promise<void> {
    const count = ${count};
    const factory = ${factoryName}.new();

    // Optionally truncate the table before seeding
    if (${truncate}) {
      // await Table.query().delete();
      // Or use: await database.raw('TRUNCATE TABLE ${tableName}');
    }

    // Generate and create records
    const records = factory.count(count);

    for (const record of records) {
      // Insert using Query Builder (recommended)
      // await ${options.modelName}.create(record);
    }

    Logger.info(\`✅ Seeded \${count} ${modelLower} records\`);
  }`;
}
/**
 * Build seeder getRecords method
 */
function buildSeederGetRecordsMethod(factoryName) {
    return `  /**
   * Get records from this seeder
   */
  async getRecords(count: number): Promise<Record<string, unknown>[]> {
    const factory = ${factoryName}.new();
    return factory.count(count);
  }`;
}
/**
 * Build seeder with states method
 */
function buildSeederWithStatesMethod(options, count, factoryName, modelLower) {
    return `  /**
   * Seed with specific states
   */
  async seedWithStates(): Promise<void> {
    const factory = ${factoryName}.new();

    // Create active records (50%)
    const active = factory.state('active').count(Math.ceil(${count} * 0.5));
    for (const record of active) {
      // await ${options.modelName}.create(record);
    }

    // Create inactive records (30%)
    const inactive = factory.state('inactive').count(Math.ceil(${count} * 0.3));
    for (const record of inactive) {
      // await ${options.modelName}.create(record);
    }

    // Create deleted records (20%)
    const deleted = factory.state('deleted').count(Math.ceil(${count} * 0.2));
    for (const record of deleted) {
      // await ${options.modelName}.create(record);
    }

    Logger.info(\`✅ Seeded ${count} ${modelLower} records with state distribution\`);
  }`;
}
/**
 * Build seeder with relationships method
 */
function buildSeederWithRelationshipsMethod(_options, count, factoryName, modelLower, relationshipMethods) {
    return `  /**
   * Seed with relationships
   */
  async seedWithRelationships(): Promise<void> {
    const factory = ${factoryName}.new();

${relationshipMethods}

    Logger.info(\`✅ Seeded ${count} ${modelLower} records with relationships\`);
  }`;
}
/**
 * Build seeder reset method
 */
function buildSeederResetMethod(options) {
    const tableName = getTableName(options.modelName);
    return `  /**
   * Reset seeder (truncate table)
   */
  async reset(): Promise<void> {
    // await database.raw('TRUNCATE TABLE ${tableName}');
    Logger.info(\`✅ Truncated ${tableName} table\`);
  }`;
}
/**
 * Build import statements
 */
function buildImports(options) {
    const factoryName = getFactoryName(options);
    return `import { Logger } from '../../config/logger';
import { ${factoryName} } from '@database/factories/${factoryName}';
import { ${options.modelName} } from '@app/Models/${options.modelName}';`;
}
/**
 * Build relationship seeding methods
 */
function buildRelationshipMethods(options) {
    if (options.relationships === undefined || options.relationships.length === 0) {
        return `    const records = factory.count(${options.count ?? 10});

    // Create records with relationships (implement as needed)
    for (const record of records) {
      // await ${options.modelName}.create(record);
    }`;
    }
    const relationshipCode = options.relationships
        .map((rel) => {
        const relId = `${CommonUtils.camelCase(rel)}Id`;
        return `    // Seed with ${rel} relationships
    const ${CommonUtils.camelCase(rel)}s = await ${rel}.all();
    if (${CommonUtils.camelCase(rel)}s.length > 0) {
      const records = factory.count(Math.min(${options.count ?? 10}, ${CommonUtils.camelCase(rel)}s.length));

      for (let i = 0; i < records.length; i++) {
        (records[i] as Record<string, unknown>).${relId} = (${CommonUtils.camelCase(rel)}s[i] as { id?: unknown }).id;
        // await ${options.modelName}.create(records[i]);
      }
    }`;
    })
        .join('\n\n');
    return relationshipCode;
}
/**
 * Get factory name from model name
 */
function getFactoryName(options) {
    return options.factoryName ?? `${options.modelName}Factory`;
}
/**
 * Get database table name from model name
 */
function getTableName(modelName) {
    // Convert PascalCase to snake_case
    return (modelName
        .replaceAll(/([A-Z])/g, '_$1')
        .toLowerCase()
        .replace(/^_/, '') + 's');
}
/**
 * Get available seeder options
 */
export function getAvailableOptions() {
    return [
        'Truncate table before seeding (default: true)',
        'Custom record count (default: 10, max: 100000)',
        'Relationship seeding',
        'State-based distribution (active, inactive, deleted)',
        'Batch operations for large datasets',
    ];
}
export const SeederGenerator = Object.freeze({
    validateOptions,
    generateSeeder,
    generateDatabaseSeeder,
    getAvailableOptions,
});
export default SeederGenerator;
