'use strict';

/**
 * Contract: endpoint create
 *
 * Generate endpoint REST baru dari payload JSON. Workflow:
 * 1. Validate args + payload format
 * 2. Register/update project in registry
 * 3. Detect conflicts, optionally archive existing files
 * 4. Generate main module + submodule + model + query files
 * 5. Update metadata (endpoint + workbench)
 * 6. Optional: generate demo files (curl, postman, insomnia)
 * 7. Optional: run audit table migration (if fieldPolicy.strategies includes 'audit')
 *
 * Inline migration v4.0.0: business logic dari create-module.js
 * dipindah ke handler. Contract flag `--name` di-map ke `endpoint` internal,
 * dan flag boolean tidak lagi via parseArguments (langsung dari validateContract).
 *
 * Hotfix v3.1.0: flag `--no-audit-migration` ditambahkan untuk skip audit
 * table migration runtime — preserved di inline version.
 */

const fs = require('fs');
const path = require('path');
const ArgumentValidator = require('../../lib/validators/argument-validator');
const PayloadValidator = require('../../lib/validators/payload-validator');
const PayloadProcessor = require('../../lib/utils/payload-processor');
const MainModuleGenerator = require('../../lib/generators/main-module-generator');
const ModelGenerator = require('../../lib/generators/model-generator');
const SubmoduleGenerator = require('../../lib/generators/submodule-generator');
const ConflictChecker = require('../../lib/utils/conflict-checker');
const MetadataManager = require('../../lib/utils/metadata-manager');
const DemoGenerator = require('../../src/utils/demo-generator');
const projectRegistry = require('../../lib/utils/project-registry');
const cliOutput = require('../../lib/utils/cli-output');
const endpointSchemaValidator = require('../../lib/payload/endpoint-schema-validator');

function hasAuditRequired(payload) {
    if (!payload || !payload.fieldPolicy) return false;
    return Object.values(payload.fieldPolicy).some(
        cfg => cfg && Array.isArray(cfg.strategies) && cfg.strategies.includes('audit')
    );
}

async function runAuditMigrationHook(payload, opts) {
    if (opts.noAuditMigration) {
        console.log('[restforge] audit migration skipped (--no-audit-migration flag)');
        const { generateAuditTableMigration } = require('../../lib/migration/audit-table-generator');
        const sql = generateAuditTableMigration(payload.tableName, opts.database);
        const sqlDir = path.join(process.cwd(), 'migrations', 'audit');
        fs.mkdirSync(sqlDir, { recursive: true });
        const sqlPath = path.join(sqlDir, `${payload.tableName}_audit.sql`);
        fs.writeFileSync(sqlPath, sql + '\n', 'utf8');
        console.log(`[restforge] audit migration written: ${path.relative(process.cwd(), sqlPath)}`);
        return;
    }

    const { runAuditMigration } = require('../../lib/migration/audit-table-runner');
    const { resolveConnectionInfo } = require('../../lib/migration/connection-resolver');

    console.log(`[restforge] fieldPolicy strategy 'audit' detected on table: ${payload.tableName}`);
    const connectionInfo = resolveConnectionInfo(opts.database, opts);
    await runAuditMigration(payload.tableName, opts.database, connectionInfo, {
        projectRoot: process.cwd()
    });
}

module.exports = {
    resource: 'endpoint',
    verb: 'create',
    description: 'Generate endpoint REST baru dari payload JSON',
    category: 'generation',
    flags: {
        project: {
            type: 'string',
            required: true,
            description: 'Nama project target'
        },
        name: {
            type: 'string',
            required: true,
            description: 'Nama endpoint yang akan dibuat'
        },
        payload: {
            type: 'string',
            required: true,
            description: 'Path atau nama file payload JSON'
        },
        database: {
            type: 'string',
            required: false,
            default: null,
            description: 'Tipe database (postgres|mysql|oracle|sqlite). Default: postgres'
        },
        config: {
            type: 'string',
            required: false,
            default: null,
            description: 'File config database (.env) untuk validasi schema payload-vs-database. Wajib kecuali `--skip-schema-check` aktif atau default config sudah di-set via `config set-default`. Fallback ke `.restforge/defaults.json` bila tidak disediakan eksplisit'
        },
        'skip-schema-check': {
            type: 'boolean',
            required: false,
            default: false,
            description: 'Lewati validasi schema database (escape hatch untuk DB offline atau maintenance). Shape RDF tetap divalidasi'
        },
        force: {
            type: 'boolean',
            required: false,
            default: false,
            description: 'Timpa file existing di output directory'
        },
        'create-examples': {
            type: 'boolean',
            required: false,
            default: true,
            description: 'Generate example files (curl, Postman, Insomnia)'
        },
        'skip-sql-validation': {
            type: 'boolean',
            required: false,
            default: false,
            description: 'Lewati validasi keyword SQL'
        },
        'no-audit-migration': {
            type: 'boolean',
            required: false,
            default: false,
            description: 'Skip eksekusi audit table migration saat payload pakai fieldPolicy strategies "audit" (file SQL tetap ditulis ke migrations/audit/)'
        },
        verbose: {
            type: 'boolean',
            required: false,
            default: false,
            description: 'Tampilkan output verbose untuk debugging'
        }
    },
    examples: [
        'npx restforge endpoint create --project=my-app --name=users --payload=users.json --config=db.env',
        'npx restforge endpoint create --project=my-app --name=orders --payload=orders.json --config=db.env --force=true',
        'npx restforge endpoint create --project=my-app --name=visitors --payload=visitors.json --skip-schema-check'
    ],
    async handler(args) {
        const startTime = Date.now();
        let muted = false;

        try {
            const project = ArgumentValidator.validateProjectName(args.project);
            const endpoint = ArgumentValidator.validateEndpointName(args.name);
            const payloadFile = ArgumentValidator.validatePayloadName(args.payload);
            const database = args.database
                ? ArgumentValidator.validateDatabaseType(args.database)
                : 'postgres';
            const force = !!args.force;
            const createExamples = !!args['create-examples'];
            const skipSqlValidation = !!args['skip-sql-validation'];
            const noAuditMigration = !!args['no-audit-migration'];
            const verbose = !!args.verbose;
            const skipSchemaCheck = !!args['skip-schema-check'];
            const configArg = typeof args.config === 'string' && args.config.trim().length > 0
                ? args.config.trim()
                : null;

            if (!verbose) {
                cliOutput.mute();
                muted = true;
            }

            console.log('Arguments to be processed:');
            console.log(`   Project: ${project}`);
            console.log(`   Endpoint: ${endpoint}`);
            console.log(`   Payload: ${payloadFile}`);
            console.log(`   Database: ${database}`);
            console.log(`   Force overwrite: ${force}`);
            console.log('');

            const cwd = process.cwd();
            const summary = {
                config: { project, endpoint, database, force },
                payload: null,
                archive: null,
                generated: [],
                demo: null
            };

            const rawPayload = PayloadValidator.readPayloadFile(payloadFile, skipSqlValidation);
            const payload = PayloadProcessor.processPayload(rawPayload, rawPayload._payloadPath, {
                moduleName: project,
                skipSqlValidation
            });

            summary.payload = {
                source: cliOutput.toRelative(rawPayload._payloadPath, cwd),
                status: 'validated',
                warnings: cliOutput.drainWarnings()
            };

            // Schema validation pre-codegen: cross-check payload terhadap struktur
            // tabel database aktual. Wajib unless --skip-schema-check di-set.
            // Drift -> throw (exitCode=1). Connection fail -> throw (exitCode=3).
            // Validasi dilakukan SEBELUM conflict-checker + archive supaya gagal
            // tanpa rotate file existing.
            if (muted) cliOutput.unmute();
            const schemaResult = await endpointSchemaValidator.validateEndpointSchema({
                payload,
                payloadFileName: path.basename(payloadFile),
                payloadFilePath: payloadFile,
                configArg,
                skipSchemaCheck,
                workingDir: cwd
            });
            if (!verbose) {
                cliOutput.mute();
                muted = true;
            }
            summary.schemaValidation = schemaResult;
            summary.config.config = configArg || null;

            const registry = projectRegistry.loadProjectRegistry();
            if (registry.projects[project]) {
                const existing = registry.projects[project];
                if (existing.database && database && existing.database !== database && !force) {
                    if (muted) cliOutput.unmute();
                    console.error(`Project "${project}" is already registered with database type "${existing.database}".`);
                    console.error(`Cannot change to "${database}" without --force.`);
                    throw new Error(`Project "${project}" already registered with database "${existing.database}"`);
                }
            }

            let effectiveForce = force;
            if (!force) {
                const shouldContinue = await ConflictChecker.handleModuleConflicts(project, endpoint, payload);
                if (!shouldContinue) return;
            }

            const conflicts = ConflictChecker.detectModuleConflicts(project, endpoint, payload);
            if (conflicts.hasConflicts) {
                const archiveFiles = ConflictChecker.createModuleArchiveFiles(conflicts, project, endpoint);
                if (archiveFiles.length > 0) {
                    summary.archive = {
                        count: archiveFiles.length,
                        generation: String(archiveFiles[0].generation).padStart(3, '0')
                    };
                }
                effectiveForce = true;
                summary.config.force = true;
            }

            MainModuleGenerator.createMainModule(project, database, { force: effectiveForce });
            const mainModulePath = path.join(cwd, 'src', 'modules', `${project}.js`);
            if (fs.existsSync(mainModulePath)) {
                summary.generated.push({
                    path: cliOutput.toRelative(mainModulePath, cwd),
                    size: cliOutput.formatSize(fs.statSync(mainModulePath).size)
                });
            }

            SubmoduleGenerator.createSubmoduleFile(project, endpoint, payload, effectiveForce, database);
            const submodulePath = path.join(cwd, 'src', 'modules', project, `${endpoint}.js`);
            if (fs.existsSync(submodulePath)) {
                summary.generated.push({
                    path: cliOutput.toRelative(submodulePath, cwd),
                    size: cliOutput.formatSize(fs.statSync(submodulePath).size)
                });
            }

            ModelGenerator.createModelFile(project, endpoint, payload, effectiveForce, database);
            const modelPath = path.join(cwd, 'src', 'models', project, `${endpoint}.js`);
            if (fs.existsSync(modelPath)) {
                summary.generated.push({
                    path: cliOutput.toRelative(modelPath, cwd),
                    size: cliOutput.formatSize(fs.statSync(modelPath).size)
                });
            }

            const queryDir = path.join(cwd, 'src', 'models', project, 'query');
            if (fs.existsSync(queryDir)) {
                const queryFiles = fs.readdirSync(queryDir).filter(f => f.startsWith(endpoint + '-'));
                if (queryFiles.length > 0) {
                    summary.generated.push({
                        path: cliOutput.toRelative(queryDir, cwd) + '/',
                        size: `${queryFiles.length} file${queryFiles.length > 1 ? 's' : ''}`
                    });
                }
            }

            MetadataManager.updateEndpointMetadata(project, endpoint, payload, database);
            MetadataManager.updateWorkbenchMetadata(project, endpoint, payload, database, 'endpoint');

            if (createExamples) {
                DemoGenerator.createDemoFiles(project, endpoint, payload, 3000);
                summary.examples = {
                    folder: `examples/${project}/${endpoint}/`,
                    formats: ['curl', 'postman', 'insomnia']
                };
            }

            if (hasAuditRequired(payload)) {
                await runAuditMigrationHook(payload, { project, endpoint, database, noAuditMigration });
            }

            projectRegistry.registerProject(project, database, endpoint);

            if (muted) {
                cliOutput.unmute();
                muted = false;
            }

            summary.duration = ((Date.now() - startTime) / 1000).toFixed(2);
            cliOutput.printCreateSummary(summary);
        } catch (error) {
            // cli-entry.js men-print `Error: <message>` ke stderr saat handler
            // re-throw (lihat handler dispatch). Jangan double-print di sini.
            if (muted) cliOutput.unmute();
            throw error;
        }
    }
};
