'use strict';

/**
 * Contract: test generate
 *
 * Generate integration test file (Jest + Supertest) untuk endpoint.
 * Sumber data berasal dari payload definition + test-data.json (global +
 * per-endpoint) yang mendukung mode `--init` untuk scaffold template.
 *
 * Inline migration v4.0.0: business logic asli dari `create-test.js`
 * di-inline langsung ke handler. Validasi argument via `TestArgumentValidator`
 * tidak lagi dipakai karena `cli-entry.js` sudah parse args + validateContract
 * memastikan required/type/default. `process.exit()` di legacy dikonversi
 * menjadi `throw new Error(...)` agar `cli-entry.js` yang handle exit code.
 */

const fs = require('node:fs');
const path = require('node:path');
const TestDataManager = require('../../src/utils/test-data-manager');
const TestGenerator = require('../../src/utils/test-generator');

function loadPayload(projectName, endpointName) {
    const cwd = process.cwd();

    const possiblePaths = [
        path.join(cwd, 'payload', `${endpointName}.json`),
        path.join(cwd, 'payload', `${endpointName}-payload.json`),
        path.join(cwd, `${endpointName}.json`)
    ];

    for (const payloadPath of possiblePaths) {
        if (fs.existsSync(payloadPath)) {
            console.log(`Using payload from: ${payloadPath}`);

            try {
                const content = fs.readFileSync(payloadPath, 'utf8');
                const payload = JSON.parse(content);

                if (!payload.fieldName || !Array.isArray(payload.fieldName)) {
                    throw new Error('Payload must contain fieldName array');
                }

                if (!payload.primaryKey) {
                    console.warn('Warning: Payload does not have primaryKey. Using "id" as default.');
                    payload.primaryKey = 'id';
                }

                if (!payload.action) {
                    console.warn('Warning: Payload does not have action. Using all actions enabled.');
                    payload.action = {
                        datatables: true, create: true, update: true, delete: true,
                        first: true, lookup: true, read: true
                    };
                }

                return payload;
            } catch (error) {
                if (error instanceof SyntaxError) {
                    throw new Error(`Payload file is not valid JSON: ${error.message}`);
                }
                throw error;
            }
        }
    }

    throw new Error(
        `Payload file for endpoint "${endpointName}" not found.\n` +
        `Searched in:\n` +
        possiblePaths.map(p => `  - ${p}`).join('\n') + '\n' +
        `Make sure the endpoint was created first with: npx restforge endpoint create`
    );
}

function printResolutionSummary(endpointName, createData, endpointData, globalData, payload, warnings) {
    const primaryKey = payload.primaryKey || 'id';

    if (!endpointData) {
        console.log(`  [endpoint] -- file not found, using global + type defaults`);
    }

    for (const [fieldName, value] of Object.entries(createData)) {
        let source = 'auto';

        if (endpointData && endpointData.create && endpointData.create[fieldName] !== undefined) {
            source = 'endpoint';
        } else if (globalData.fieldDefaults && globalData.fieldDefaults[fieldName]) {
            source = 'global';
        } else {
            source = 'type';
        }

        const displayValue = typeof value === 'string' ? `"${value}"` : value;
        console.log(`  [${source.padEnd(8)}] ${fieldName.padEnd(20)} --> ${displayValue}`);
    }

    const constraintsMap = new Map();
    if (Array.isArray(payload.fieldValidation)) {
        for (const f of payload.fieldValidation) {
            if (f.name) constraintsMap.set(f.name, f.constraints || {});
        }
    }

    for (const fieldName of payload.fieldName) {
        const constraints = constraintsMap.get(fieldName) || {};
        if (constraints.autoGenerate || (fieldName === primaryKey && constraints.primaryKey)) {
            console.log(`  * ${fieldName} --> skipped (autoGenerate)`);
        }
    }

    if (warnings.length > 0) {
        console.log('');
        console.log(`  WARNING: ${warnings.length} field(s) using type defaults. For precise testing:`);
        console.log(`  --> Run: npx restforge test generate --init --project=<name> --endpoint=${endpointName}`);
        console.log(`  --> Edit: tests/data/${endpointName}.test-data.json`);
    }
}

function handleInit(args) {
    console.log('Initializing test data files...');
    console.log('');

    if (args.force) {
        TestDataManager.initGlobalTestDataForce(args.force);
    } else {
        TestDataManager.initGlobalTestData();
    }

    if (args.endpoint && args.project) {
        console.log('');
        const payload = loadPayload(args.project, args.endpoint);
        if (args.force) {
            TestDataManager.initEndpointTestDataForce(args.endpoint, payload, args.force);
        } else {
            TestDataManager.initEndpointTestData(args.endpoint, payload);
        }
    }

    console.log('');
    console.log('Edit the generated files to define your test data.');
    if (!args.endpoint) {
        console.log('Tip: Use --endpoint=<name> --project=<name> with --init to also create per-endpoint template.');
    }
}

module.exports = {
    resource: 'test',
    verb: 'generate',
    description: 'Generate integration test file (Jest + Supertest) untuk endpoint',
    category: 'generation',
    flags: {
        project: {
            type: 'string',
            required: true,
            description: 'Nama project target'
        },
        endpoint: {
            type: 'string',
            required: true,
            description: 'Nama endpoint yang akan di-test'
        },
        port: {
            type: 'number',
            required: false,
            default: 3000,
            description: 'Port server saat test dijalankan'
        },
        init: {
            type: 'boolean',
            required: false,
            default: false,
            description: 'Inisialisasi konfigurasi test-data.json (global + per-endpoint jika endpoint diisi)'
        },
        force: {
            type: 'boolean',
            required: false,
            default: false,
            description: 'Timpa file test existing'
        }
    },
    examples: [
        'npx restforge test generate --project=my-app --endpoint=users --init',
        'npx restforge test generate --project=my-app --endpoint=orders --port=3032',
        'npx restforge test generate --project=my-app --endpoint=orders --force'
    ],
    async handler(args) {
        console.log('Test generator arguments:');
        if (args.project) console.log(`   Project: ${args.project}`);
        if (args.endpoint) console.log(`   Endpoint: ${args.endpoint}`);
        console.log(`   Port: ${args.port}`);
        console.log(`   Init: ${args.init}`);
        console.log(`   Force: ${args.force}`);
        console.log('');

        if (args.init) {
            handleInit(args);
            return;
        }

        const payload = loadPayload(args.project, args.endpoint);

        const testFilePath = path.join(process.cwd(), 'tests', 'integration', `${args.endpoint}.test.js`);
        if (fs.existsSync(testFilePath) && !args.force) {
            console.error(`Error: tests/integration/${args.endpoint}.test.js already exists.`);
            console.error('Use --force=true to overwrite.');
            throw new Error(`Test file tests/integration/${args.endpoint}.test.js already exists`);
        }

        const globalData = TestDataManager.loadGlobalTestData();
        const endpointData = TestDataManager.loadEndpointTestData(args.endpoint);

        console.log(`Resolving test data for ${args.endpoint}...`);
        const { create, update, warnings } = TestDataManager.resolveAllFields(
            payload.fieldName,
            payload.fieldValidation,
            endpointData,
            globalData,
            payload
        );

        printResolutionSummary(args.endpoint, create, endpointData, globalData, payload, warnings);

        console.log('');
        TestGenerator.createTestFiles(args.project, args.endpoint, payload, args.port, { create, update, warnings });

        TestGenerator.updatePackageJson(args.endpoint);

        console.log('');
        console.log('Operation completed successfully!');
        console.log(`Run: npm install && npm run test:${args.endpoint}`);
    }
};
