'use strict';

/**
 * Contract: processor create
 *
 * Generate processor module dari payload JSON. Workflow:
 * 1. Validate payload structure (processor array, methods, fields)
 * 2. Archive existing processor files jika --force
 * 3. Generate endpoint router (selalu regenerate) dengan auto-hardening
 *    (validation + sensitive masker) per processor
 * 4. Generate processor scaffolds (--force = archive + overwrite, default = skip)
 * 5. Update metadata + workbench
 *
 * Inline migration v4.0.0: business logic dari create-processor.js
 * (1143 baris) dipindah ke handler. ArgumentValidator.parseArguments
 * dihilangkan; args langsung dari validateContract.
 *
 * Module-level state preserved: isBun/isCompiledBinary deteksi deterministic.
 * Working directory di-call di dalam handler (depend cwd, fresh per invocation).
 */

const fs = require('fs');
const path = require('path');
const ArgumentValidator = require('../../lib/validators/argument-validator');
const ConflictChecker = require('../../lib/utils/conflict-checker');
const MetadataManager = require('../../lib/utils/metadata-manager');
const SensitiveFieldMasker = require('../../lib/utils/sensitive-field-masker');
const ProcessorValidationGenerator = require('../../lib/generators/processor-validation-generator');
const { validateSafeName } = require('../../lib/utils/path-validator');

const isBun = typeof Bun !== 'undefined';

function detectBunCompiled() {
    if (!isBun) return false;
    const hasVirtualFS = process.argv[1] && process.argv[1].includes('$bunfs');
    const execPathLower = process.execPath.toLowerCase();
    const isBunStandardPath = execPathLower.includes('bun') &&
        (execPathLower.endsWith('bun') || execPathLower.endsWith('bun.exe'));
    const isRestforgeBinary = execPathLower.includes('restforge');
    return hasVirtualFS || isRestforgeBinary || !isBunStandardPath;
}

const isCompiledBinary = detectBunCompiled();
const isScript = !isCompiledBinary;

function getWorkingDirectory() {
    if (isCompiledBinary) {
        const exeDir = path.dirname(process.execPath);
        if (exeDir.includes('node_modules')) {
            return process.cwd();
        }
        return exeDir;
    }
    return process.cwd();
}

function sanitizeProcessorName(name) {
    if (!name || typeof name !== 'string') {
        return '';
    }
    const reservedKeywords = [
        'break', 'case', 'catch', 'class', 'const', 'continue', 'debugger', 'default',
        'delete', 'do', 'else', 'export', 'extends', 'false', 'finally', 'for',
        'function', 'if', 'import', 'in', 'instanceof', 'new', 'null', 'return',
        'super', 'switch', 'this', 'throw', 'true', 'try', 'typeof', 'var',
        'void', 'while', 'with', 'yield', 'let', 'static', 'enum', 'implements',
        'package', 'protected', 'interface', 'private', 'public'
    ];
    let sanitized = name
        .replace(/(?:^\w|[A-Z]|\b\w)/g, (word, index) => {
            return index === 0 ? word.toLowerCase() : word.toUpperCase();
        })
        .replace(/\s+/g, '')
        .replace(/[-_]/g, '');
    if (reservedKeywords.includes(sanitized.toLowerCase())) {
        sanitized = sanitized + 'Processor';
    }
    return sanitized;
}

function validatePayload(payload) {
    const errors = [];
    if (!payload.processor || !Array.isArray(payload.processor)) {
        errors.push('Payload harus memiliki properti "processor" berupa array');
        return { valid: false, errors };
    }
    const validTypes = ['string', 'number', 'integer', 'boolean', 'uuid', 'date', 'datetime', 'array', 'object'];
    const validMethods = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'];
    payload.processor.forEach((proc, index) => {
        const prefix = `Processor[${index}] "${proc.name || 'unnamed'}"`;
        if (!proc.name || typeof proc.name !== 'string') {
            errors.push(`${prefix}: Harus memiliki properti "name" berupa string`);
        }
        if (!proc.method || !validMethods.includes(proc.method.toUpperCase())) {
            errors.push(`${prefix}: Harus memiliki properti "method" (${validMethods.join(', ')})`);
        }
        if (proc.sql && (proc.sql.query || proc.sql.file)) {
            if (proc.sql.params && !Array.isArray(proc.sql.params)) {
                errors.push(`${prefix}: SQL "params" harus berupa array jika disediakan`);
            }
        }
        if (proc.request) {
            if (proc.request.validate !== undefined && typeof proc.request.validate !== 'boolean') {
                errors.push(`${prefix}: request.validate harus boolean (default true)`);
            }
            const validFormats = ['email', 'url', 'phone-id', 'uuid'];
            ['body', 'params', 'headers'].forEach(source => {
                if (proc.request[source]) {
                    Object.entries(proc.request[source]).forEach(([fieldName, fieldDef]) => {
                        if (!fieldDef || typeof fieldDef !== 'object') return;
                        if (fieldDef.type && !validTypes.includes(fieldDef.type)) {
                            errors.push(`${prefix}: Field "${fieldName}" memiliki type tidak valid "${fieldDef.type}"`);
                        }
                        if (fieldDef.sensitive !== undefined && typeof fieldDef.sensitive !== 'boolean') {
                            errors.push(`${prefix}: Field "${fieldName}" property "sensitive" harus boolean`);
                        }
                        if (fieldDef.format !== undefined) {
                            if (typeof fieldDef.format !== 'string' || !validFormats.includes(fieldDef.format)) {
                                errors.push(`${prefix}: Field "${fieldName}" property "format" harus salah satu: ${validFormats.join(', ')}`);
                            }
                        }
                        if (fieldDef.minLength !== undefined && (!Number.isInteger(fieldDef.minLength) || fieldDef.minLength < 0)) {
                            errors.push(`${prefix}: Field "${fieldName}" property "minLength" harus integer >= 0`);
                        }
                        if (fieldDef.maxLength !== undefined && (!Number.isInteger(fieldDef.maxLength) || fieldDef.maxLength < 0)) {
                            errors.push(`${prefix}: Field "${fieldName}" property "maxLength" harus integer >= 0`);
                        }
                        if (fieldDef.enum !== undefined && !Array.isArray(fieldDef.enum)) {
                            errors.push(`${prefix}: Field "${fieldName}" property "enum" harus berupa array`);
                        }
                    });
                }
            });
        }
        if (proc.response && proc.response.message) {
            if (typeof proc.response.message !== 'object') {
                errors.push(`${prefix}: response.message harus berupa object`);
            }
        }
    });
    return { valid: errors.length === 0, errors };
}

function readSqlFile(sqlFilePath, payloadDir) {
    const fullPath = path.join(payloadDir, sqlFilePath);
    if (!fs.existsSync(fullPath)) {
        throw new Error(`File SQL tidak ditemukan: ${fullPath}`);
    }
    return fs.readFileSync(fullPath, 'utf8').trim();
}

if (isCompiledBinary) {
    process.removeAllListeners('warning');
}

function ensureDirectoryExists(dirPath) {
    try {
        if (!fs.existsSync(dirPath)) {
            fs.mkdirSync(dirPath, { recursive: true });
            console.log(`Directory ${dirPath} created successfully.`);
        }
    } catch (error) {
        console.error(`Error creating directory ${dirPath}:`, error.message);
        throw error;
    }
}

function readPayloadFile(payloadFileName, workingDir) {
    const possiblePaths = [
        path.join(workingDir, 'payload', payloadFileName),
        path.join(workingDir, payloadFileName),
        path.join(process.cwd(), 'payload', payloadFileName),
        path.join(process.cwd(), payloadFileName)
    ];

    if (isScript) {
        possiblePaths.unshift(path.join(__dirname, 'payload', payloadFileName));
    }

    let payloadPath = null;
    for (const testPath of possiblePaths) {
        if (fs.existsSync(testPath)) {
            payloadPath = testPath;
            break;
        }
    }

    if (!payloadPath) {
        console.error(`Error: Payload file ${payloadFileName} not found in the following locations:`);
        possiblePaths.forEach(p => console.error(`  - ${p}`));
        throw new Error(`Payload file ${payloadFileName} not found`);
    }

    console.log(`Using payload from: ${payloadPath}`);
    const payloadDir = path.dirname(payloadPath);

    const payloadContent = fs.readFileSync(payloadPath, 'utf8');
    const payload = JSON.parse(payloadContent);
    payload._payloadDir = payloadDir;

    const validation = validatePayload(payload);
    if (!validation.valid) {
        console.error('Error: Payload is not valid:');
        validation.errors.forEach(err => console.error(`  - ${err}`));
        throw new Error('Payload is not valid: ' + validation.errors.join('; '));
    }

    return payload;
}

function createOrCheckModule(moduleName, workingDir) {
    const modulesDir = path.join(workingDir, 'src', 'modules');
    ensureDirectoryExists(modulesDir);

    const moduleFilePath = path.join(modulesDir, `${moduleName}.js`);

    if (fs.existsSync(moduleFilePath)) {
        console.log(`File module ${moduleName}.js already exists.`);
    } else {
        console.log(`File module ${moduleName}.js not found. Creating new module...`);

        const moduleContent = `const express = require('express');
const bodyParser = require('body-parser');
const path = require('path');
const fs = require('fs');
const corsMiddleware = require('@restforgejs/platform/src/middleware/cors');
const securityHeaders = require('@restforgejs/platform/src/middleware/security-headers');

/**
 * Fungsi untuk mengeksekusi modul ${moduleName}
 * @param {Object} config - Konfigurasi untuk menjalankan modul
 * @param {number} config.port - Port untuk server
 * @param {string} config.key - API Key (opsional)
 * @returns {Promise<void>} Promise yang tidak pernah resolve agar server tetap berjalan
 */
async function execute(config) {
  return new Promise((resolve) => {
    const app = express();
    const port = config.port || 3000;

    // CORS middleware (konfigurasi via CORS_ENABLED dan CORS_ORIGINS di .env)
    app.use(corsMiddleware.middleware());

    // Security headers middleware (konfigurasi via HELMET_ENABLED di .env)
    app.use(securityHeaders.middleware());

    // Middleware untuk parsing JSON
    app.use(bodyParser.json({
      verify: (req, res, buf, encoding) => {
        if (buf.length === 0) {
          return;
        }
        try {
          JSON.parse(buf);
        } catch (error) {
          res.status(400).json({
            success: false,
            error: 'Invalid JSON payload',
            message: 'The payload sent is not a valid JSON format',
            details: error.message
          });
          throw new Error('Invalid JSON');
        }
      }
    }));

    // Middleware untuk parsing URL-encoded data
    app.use(bodyParser.urlencoded({ extended: true }));

    // Middleware untuk logging
    app.use((req, res, next) => {
      console.log(\`\$\{new Date().toISOString()} - \$\{req.method} \$\{req.url}\`);
      next();
    });

    // Middleware untuk validasi API key jika diperlukan (constant-time comparison)
    if (config.key) {
      const crypto = require('crypto');
      const expectedKey = Buffer.from(config.key);

      app.use((req, res, next) => {
        const apiKey = req.headers['x-api-key'];
        if (!apiKey) {
          return res.status(401).json({ error: 'Unauthorized: Invalid API Key' });
        }
        const providedKey = Buffer.from(apiKey);
        if (expectedKey.length !== providedKey.length ||
            !crypto.timingSafeEqual(expectedKey, providedKey)) {
          return res.status(401).json({ error: 'Unauthorized: Invalid API Key' });
        }
        next();
      });
    }

    // Health check endpoint
    app.get('/api/${moduleName}/health', (req, res) => {
      res.json({
        status: 'ok',
        timestamp: new Date().toISOString().replace('T', ' ').replace(/\\.\\d{3}Z$/, ''),
        service: '${moduleName}'
      });
    });

    // Muat semua rute dari folder ${moduleName}
    const modulesDir = path.join(__dirname, '${moduleName}');

    try {
      // Periksa apakah direktori ada
      if (!fs.existsSync(modulesDir)) {
        fs.mkdirSync(modulesDir, { recursive: true });
        console.log(\`Direktori \$\{modulesDir} berhasil dibuat\`);
      }

      // Baca semua file di direktori modul
      const files = fs.readdirSync(modulesDir);

      // Daftarkan semua endpoint dari modul-modul
      files.forEach(file => {
        if (file.endsWith('.js')) {
          try {
            const moduleName = file.replace('.js', '');
            const moduleRoutes = require(path.join(modulesDir, file));

            // Tambahkan prefix untuk semua rute modul
            app.use(\`/api/${moduleName}/\$\{moduleName}\`, moduleRoutes);
            console.log(\`Modul \$\{moduleName} berhasil dimuat dari ${moduleName}\`);
          } catch (error) {
            console.error(\`Error saat memuat modul \$\{file} dari ${moduleName}:\`, error);
          }
        }
      });

      // Tambahkan rute default
      app.get('/', (req, res) => {
        res.json({
          message: '${moduleName} API',
          status: 'running'
        });
      });

      // Error handling middleware
      app.use((err, req, res, next) => {
        console.error('Error:', err);

        if (!res.headersSent) {
          return res.status(500).json({
            success: false,
            error: 'Internal Server Error',
            message: 'An error occurred on the server',
            details: err.message
          });
        }

        next(err);
      });

      // Handler untuk rute tidak ditemukan
      app.use((req, res) => {
        res.status(403).json({
          success: false,
          error: 'Forbidden',
          message: 'Access to the requested resource is forbidden',
          timestamp: new Date().toISOString()
        });
      });

      // Mulai server
      app.listen(port, () => {
        console.log(\`${moduleName} server berjalan di port \$\{port}\`);
      });

      // Tangani sinyal terminasi
      process.on('SIGINT', () => {
        console.log('Menerima sinyal SIGINT, shutting down...');
        process.exit(0);
      });

      process.on('SIGTERM', () => {
        console.log('Menerima sinyal SIGTERM, shutting down...');
        process.exit(0);
      });

    } catch (error) {
      console.error(\`Error saat menjalankan modul ${moduleName}:\`, error);
      resolve();
    }
  });
}

module.exports = { execute };`;

        fs.writeFileSync(moduleFilePath, moduleContent);
        console.log(`File module ${moduleName}.js created successfully.`);
    }

    const moduleDir = path.join(modulesDir, moduleName);
    ensureDirectoryExists(moduleDir);
    return moduleDir;
}

function createEndpointFile(moduleDir, moduleName, endpointName, payload) {
    const endpointFilePath = path.join(moduleDir, `${endpointName}.js`);

    const endpointExists = fs.existsSync(endpointFilePath);
    if (endpointExists) {
        console.log(`  [UPDATE] ${endpointName}.js — endpoint router di-regenerate.`);
    }

    const importProcessors = payload.processor.map(proc => {
        const varName = sanitizeProcessorName(proc.name);
        return `const ${varName} = require('./processor/${endpointName}/${proc.name}');`;
    }).join('\n');

    const routeProcessors = payload.processor.map(proc => {
        const varName = sanitizeProcessorName(proc.name);
        const method = proc.method.toLowerCase();
        const hasParams = proc.request && proc.request.params;

        let routePath = `/${proc.name}`;
        if (hasParams) {
            const paramKeys = Object.keys(proc.request.params);
            paramKeys.forEach(param => {
                routePath += `/:${param}`;
            });
        }
        if (!routePath) routePath = '/';

        let hasCache = proc.cache && proc.cache.enabled === true;

        if (hasCache) {
            const mutationMethods = ['put', 'delete', 'patch'];
            const isMutationMethod = mutationMethods.includes(method);
            const sqlQuery = (proc.sql?.query || '').toUpperCase().trim();
            const mutationKeywords = ['INSERT ', 'UPDATE ', 'DELETE ', 'ALTER ', 'DROP ', 'TRUNCATE '];
            const isMutationSql = mutationKeywords.some(kw => sqlQuery.includes(kw));

            if (isMutationMethod || isMutationSql) {
                const reason = isMutationMethod
                    ? `method ${method.toUpperCase()} is a mutation operation`
                    : `SQL contains mutation statement`;
                console.log(`  [WARN] cache.enabled ignored for processor "${proc.name}" — ${reason}. Cache only applies to read (SELECT) operations.`);
                hasCache = false;
            }
        }

        const cacheTTL = hasCache ? (proc.cache.ttl || 300) : 0;

        const cacheCheckCode = hasCache ? `
    // --- Cache check (auto-generated dari payload cache config) ---
    let cacheKey = null;
    if (services.cache && services.cache.isEnabled()) {
      const crypto = require('crypto');
      const hash = crypto.createHash('md5')
        .update(JSON.stringify(input))
        .digest('hex').substring(0, 8);

      cacheKey = services.cache.buildKey({
        module: '${moduleName}',
        endpoint: '${endpointName}',
        type: '${proc.name}',
        id: hash
      });

      const cached = await services.cache.get(cacheKey);
      if (cached) return res.status(cached.statusCode || 200).json(cached);
    }` : '';

        const cacheSetCode = hasCache ? `

    // --- Cache set (hanya untuk success response) ---
    if (cacheKey && result.success) {
      await services.cache.set(cacheKey, result, ${cacheTTL});
    }` : '';

        return `// Route untuk processor ${proc.name} - ${proc.description || ''}
// SECURITY: Validation di-enforce di router layer (auto-generated dari payload request schema).
//           JANGAN edit file router ini. Perubahan akan tertimpa saat generate ulang.
router.${method}('${routePath}', async (req, res) => {
  try {
    const input = { ...req.body, ...req.params, ...req.query };

    // Header mapping dengan type check
    ${generateHeaderMapping(proc)}

${ProcessorValidationGenerator.generateRouterValidationCode(proc)}

    // Debug log dengan sensitive fields masked
    if (process.env.NODE_ENV !== 'production') {
      console.log('[${proc.name}] Input:', JSON.stringify(__maskSensitive(input), null, 2));
    }
${cacheCheckCode}

    const result = await ${varName}.process(input, services, req);
${cacheSetCode}

    return res.status(result.statusCode || 200).json(result);
  } catch (error) {
    console.error('[${proc.name}] Error:', error.message);
    return res.status(500).json({
      success: false,
      message: '${proc.response?.message?.error || 'Terjadi kesalahan pada server'}',
      data: [],
      timestamp: new Date().toISOString()
    });
  }
});`;
    }).join('\n\n');

    const extraSensitiveKeywords = SensitiveFieldMasker.collectExplicitSensitiveKeywords(payload.processor);
    const maskerHelperBlock = `\n// === Auto-generated sensitive field masker (lihat secure-by-default.md Anti-Pattern 7) ===\n${SensitiveFieldMasker.buildMaskerHelperCode({ extraKeywords: extraSensitiveKeywords })}\n`;

    const endpointContent = `const express = require('express');
const router = express.Router();
const { resolveServices } = require('@restforgejs/platform/src/utils/service-resolver');
const services = resolveServices();
${importProcessors}

// CORS ditangani di level app oleh cors middleware (lihat konfigurasi CORS_ENABLED dan CORS_ORIGINS di .env)
${maskerHelperBlock}
${routeProcessors}

module.exports = router;`;

    fs.writeFileSync(endpointFilePath, endpointContent);
    if (!endpointExists) {
        console.log(`  [NEW] ${endpointName}.js — endpoint router created successfully.`);
    }
}

function generateHeaderMapping(proc) {
    if (!proc.request || !proc.request.headers) {
        return '// Tidak ada header mapping';
    }
    const mappings = [];
    Object.entries(proc.request.headers).forEach(([headerName, headerDef]) => {
        if (headerDef.mapTo) {
            const normalizedHeader = headerName.toLowerCase();
            mappings.push(`if (typeof req.headers['${normalizedHeader}'] === 'string' && req.headers['${normalizedHeader}'].length > 0) {
      input['${headerDef.mapTo}'] = req.headers['${normalizedHeader}'];
    }`);
        }
    });
    return mappings.length > 0 ? mappings.join('\n    ') : '// Tidak ada header mapping';
}

function createProcessorFiles(moduleDir, moduleName, endpointName, payload, force = false) {
    const processorDir = path.join(moduleDir, 'processor');
    ensureDirectoryExists(processorDir);

    const endpointProcessorDir = path.join(processorDir, endpointName);
    ensureDirectoryExists(endpointProcessorDir);

    let createdCount = 0;
    let skippedCount = 0;
    let archivedCount = 0;

    const processors = payload.processor;
    const payloadDir = payload._payloadDir;

    for (const proc of processors) {
        const processorFilePath = path.join(endpointProcessorDir, `${proc.name}.js`);

        if (fs.existsSync(processorFilePath)) {
            if (!force) {
                console.log(`  [SKIP] ${proc.name}.js — file already exists, custom code preserved.`);
                skippedCount++;
                continue;
            }
            console.log(`  [ARCHIVE+OVERWRITE] ${proc.name}.js — file archived then regenerated.`);
            archivedCount++;
        }

        const processorContent = generateProcessor(proc, payloadDir);
        fs.writeFileSync(processorFilePath, processorContent);
        console.log(`  [NEW] ${proc.name}.js — scaffold created successfully.`);
        createdCount++;
    }

    console.log('');
    console.log(`Summary: ${createdCount} processor created, ${skippedCount} preserved, ${archivedCount} archived.`);
}

function generateProcessor(proc, payloadDir) {
    if (!proc.sql || (!proc.sql.query && !proc.sql.file)) {
        return generateProcessorNoSql(proc);
    }

    const varName = sanitizeProcessorName(proc.name);

    let sqlQuery;
    if (proc.sql.file) {
        sqlQuery = readSqlFile(proc.sql.file, payloadDir);
    } else {
        sqlQuery = proc.sql.query;
    }

    const escapedSql = sqlQuery.replace(/`/g, '\\`').replace(/\$\{/g, '\\${');
    const validationCode = generateValidationCode(proc);
    const paramsCode = generateSqlParamsCode(proc);

    const successMessage = proc.response?.message?.success || 'Operasi berhasil';
    const emptyMessage = proc.response?.message?.empty || 'Data tidak ditemukan';
    const errorMessage = proc.response?.message?.error || 'Terjadi kesalahan';

    return `/**
 * Processor: ${proc.name}
 * ${proc.description || ''}
 * Method: ${proc.method}
 */
const ${varName} = {
  /**
   * Memproses request dan menjalankan query SQL.
   *
   * SECURITY BOUNDARY (jangan dilemahkan saat refactor):
   *   1. Router sudah enforce validation berdasarkan payload.request schema (type, required, format).
   *      Validasi tambahan (business rule, range, whitelist nilai) tetap tanggung jawab processor ini.
   *   2. Semua nilai user-controlled WAJIB masuk SQL via parameterized placeholder (\\$1, ?, :1).
   *      Concat user input ke SQL string DILARANG (lihat Anti-Pattern 1 di secure-by-default.md).
   *   3. Identifier dinamis (kolom/tabel) WAJIB di-whitelist sebelum di-pass ke db.table() / raw SQL.
   *   4. Field sensitif (password, token, secret, credit_card, dll.) JANGAN di-log plaintext.
   *      Logger di RESTForge tidak melakukan auto-masking.
   *   5. Error response ke client harus generic. JANGAN return error.message atau error.stack.
   *
   * Trust source: input bersumber dari req.body/params/query/headers (Zero Trust).
   * Detail pattern: docs/features/feat-processor.md section "Keamanan (Security)".
   *
   * @param {Object} input - Data input dari request (body, params, headers)
   * @param {Object} services - Injected services { db, logger, redis, kafka, cache, createResponse, createError, createValidationError }
   * @param {Object} req - Express request object (headers, ip, method, originalUrl)
   * @returns {Promise<Object>} Response standar {success, statusCode, message, data, timestamp}
   */
  async process(input, services, req) {
    const { db, logger, createResponse, createError } = services;
    try {
      // Note: Required-field dan type validation sudah di-enforce di router (auto-generated).
      // Tambahkan business rule validation di sini bila diperlukan (status check, authorization).
${validationCode}

      // SQL Query (template parameterized; placeholder diisi dari array params)
      const sql = \`${escapedSql}\`;

      // Susun parameter untuk query
${paramsCode}

      // === SECURITY: SQL executed dengan parameterized placeholder (params array di atas).
      //     JANGAN modify menjadi string concat. Lihat Anti-Pattern 1 di secure-by-default.md.
      logger.debug({ paramCount: Array.isArray(params) ? params.length : 0 }, '[${proc.name}] Executing SQL');

      // Eksekusi query
      const result = await db.executeQuery(sql, params);

      // Handle response berdasarkan hasil
      if (!result || result.length === 0) {
        return createResponse(200, '${emptyMessage}', []);
      }

      return createResponse(200, '${successMessage}', result);
    } catch (error) {
      // === SECURITY: Generic message ke client, full detail di server log. ===
      logger.error({ event: '${proc.name}_error', error: error.message, stack: error.stack }, '[${proc.name}] Error');
      throw new Error('${errorMessage}');
    }
  }
};

module.exports = ${varName};`;
}

function generateProcessorNoSql(proc) {
    const varName = sanitizeProcessorName(proc.name);
    const validationCode = generateValidationCode(proc);
    const successMessage = proc.response?.message?.success || 'Operation successful.';
    const errorMessage = proc.response?.message?.error || 'Failed to process request.';

    let inputDestructuring = '// const { fieldName } = input;';
    if (proc.request) {
        const fields = [];
        if (proc.request.body) fields.push(...Object.keys(proc.request.body));
        if (proc.request.params) fields.push(...Object.keys(proc.request.params));
        if (fields.length > 0) {
            inputDestructuring = `const { ${fields.join(', ')} } = input;`;
        }
    }

    return `/**
 * Processor: ${proc.name}
 * ${proc.description || ''}
 * Method: ${proc.method}
 */
const ${varName} = {
  /**
   * Process incoming request and execute business logic.
   *
   * SECURITY BOUNDARY (jangan dilemahkan saat refactor):
   *   1. Router sudah enforce validation berdasarkan payload.request schema (type, required, format).
   *      Validasi tambahan (business rule, range, whitelist nilai) tetap tanggung jawab processor ini.
   *   2. Semua nilai user-controlled WAJIB masuk SQL via parameterized placeholder (\\$1, ?, :1).
   *      Concat user input ke SQL string DILARANG (lihat Anti-Pattern 1 di secure-by-default.md).
   *   3. Identifier dinamis (kolom/tabel) WAJIB di-whitelist sebelum di-pass ke db.table() / raw SQL.
   *   4. Field sensitif (password, token, secret, credit_card, dll.) JANGAN di-log plaintext.
   *      Logger di RESTForge tidak melakukan auto-masking.
   *   5. Error response ke client harus generic. JANGAN return error.message atau error.stack.
   *
   * Trust source: input bersumber dari req.body/params/query/headers (Zero Trust).
   * Detail pattern: docs/features/feat-processor.md section "Keamanan (Security)".
   *
   * @param {Object} input - Request data (body, params, headers)
   * @param {Object} services - Injected services { db, logger, redis, kafka, cache, createResponse, createError, createValidationError }
   * @param {Object} req - Express request object (headers, ip, method, originalUrl)
   * @returns {Promise<Object>} Standard response {success, statusCode, message, data, timestamp}
   */
  async process(input, services, req) {
    const { db, logger, createResponse, createError } = services;

    try {
      // Note: Required-field dan type validation sudah di-enforce di router (auto-generated).
      // Tambahkan business rule validation di sini bila diperlukan (status check, authorization).
${validationCode}

      ${inputDestructuring}

      // TODO: implement business logic here

      // === SECURITY: Parameterized query wajib. Jangan concat user input. ===
      // Contoh query builder:
      // const result = await db.table('schema.table_name')
      //   .where('column', value)
      //   .get();

      // Contoh raw query (gunakan placeholder \\$1, ?, :1 sesuai dialect):
      // const result = await db.executeQuery(
      //   'SELECT * FROM schema.table_name WHERE id = \\$1',
      //   [id]
      // );

      // === SECURITY: Untuk dynamic column/table, whitelist dulu sebelum ke Query Builder. ===
      // const VALID_SORT = ['created_at', 'updated_at'];
      // if (input.sort_by && !VALID_SORT.includes(input.sort_by)) {
      //   return createError(400, 'sort_by tidak valid');
      // }

      // Contoh transaction:
      // const result = await db.executeTransaction([
      //   { sql: 'INSERT INTO ...', params: [...] },
      //   { sql: 'UPDATE ...', params: [...] }
      // ]);

      // Placeholder — ganti dengan implementasi sebenarnya
      const result = [];

      return createResponse(200, '${successMessage}', result);

    } catch (error) {
      // === SECURITY: Generic message ke client, full detail di server log. ===
      logger.error({ event: '${proc.name}_error', error: error.message, stack: error.stack }, 'Failed to process ${proc.name}.');
      return createError(500, '${errorMessage}');
    }
  }
};

module.exports = ${varName};`;
}

function generateValidationCode(proc) {
    if (!proc.request) {
        return '      // Tidak ada validasi yang diperlukan';
    }
    const validations = [];
    ['body', 'params', 'headers'].forEach(source => {
        if (proc.request[source]) {
            Object.entries(proc.request[source]).forEach(([fieldName, fieldDef]) => {
                if (fieldDef.required) {
                    const inputField = source === 'headers' && fieldDef.mapTo ? fieldDef.mapTo : fieldName;
                    validations.push(`      if (input['${inputField}'] === undefined || input['${inputField}'] === null || input['${inputField}'] === '') {
        throw new Error('Field "${inputField}" wajib diisi');
      }`);
                    if (fieldDef.type) {
                        const typeValidation = generateTypeValidation(inputField, fieldDef.type);
                        if (typeValidation) {
                            validations.push(typeValidation);
                        }
                    }
                }
            });
        }
    });
    return validations.length > 0 ? validations.join('\n\n') : '      // Tidak ada validasi yang diperlukan';
}

function generateTypeValidation(fieldName, type) {
    switch (type) {
        case 'uuid':
            return `      if (input['${fieldName}'] && !/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(input['${fieldName}'])) {
        throw new Error('Field "${fieldName}" harus berupa UUID yang valid');
      }`;
        case 'number':
            return `      if (input['${fieldName}'] !== undefined && input['${fieldName}'] !== null && input['${fieldName}'] !== '' && isNaN(Number(input['${fieldName}']))) {
        throw new Error('Field "${fieldName}" harus berupa angka');
      }`;
        case 'integer':
            return `      if (input['${fieldName}'] !== undefined && input['${fieldName}'] !== null && input['${fieldName}'] !== '' && !Number.isInteger(Number(input['${fieldName}']))) {
        throw new Error('Field "${fieldName}" harus berupa bilangan bulat');
      }`;
        case 'boolean':
            return `      if (input['${fieldName}'] !== undefined && input['${fieldName}'] !== null && typeof input['${fieldName}'] !== 'boolean' && input['${fieldName}'] !== 'true' && input['${fieldName}'] !== 'false') {
        throw new Error('Field "${fieldName}" harus berupa boolean');
      }`;
        default:
            return null;
    }
}

function generateSqlParamsCode(proc) {
    if (!proc.sql.params || proc.sql.params.length === 0) {
        return '      const params = [];';
    }
    const paramLines = proc.sql.params.map(paramName => {
        let defaultValue = 'null';
        let fieldDef = null;
        if (proc.request) {
            if (proc.request.body && proc.request.body[paramName]) {
                fieldDef = proc.request.body[paramName];
            } else if (proc.request.params && proc.request.params[paramName]) {
                fieldDef = proc.request.params[paramName];
            } else if (proc.request.headers) {
                Object.entries(proc.request.headers).forEach(([headerName, headerDef]) => {
                    if (headerDef.mapTo === paramName) {
                        fieldDef = headerDef;
                    }
                });
            }
        }
        if (fieldDef && fieldDef.default !== undefined) {
            if (typeof fieldDef.default === 'string') {
                defaultValue = `'${fieldDef.default}'`;
            } else {
                defaultValue = fieldDef.default;
            }
        }
        return `        input['${paramName}'] !== undefined && input['${paramName}'] !== '' ? input['${paramName}'] : ${defaultValue}`;
    });
    return `      const params = [
${paramLines.join(',\n')}
      ];`;
}

function updateEndpointMetadata(moduleName, endpointName, payload) {
    const processorPayload = {
        type: 'processor',
        processors: payload.processor.map(proc => proc.name),
        processorCount: payload.processor.length,
        path: `src/modules/${moduleName}/${endpointName}.js`
    };
    MetadataManager.updateEndpointMetadata(moduleName, endpointName, processorPayload, 'postgres');
}

module.exports = {
    resource: 'processor',
    verb: 'create',
    description: 'Generate processor dari payload JSON',
    category: 'generation',
    flags: {
        project: {
            type: 'string',
            required: true,
            description: 'Nama project target'
        },
        name: {
            type: 'string',
            required: true,
            description: 'Nama processor (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'
        },
        force: {
            type: 'boolean',
            required: false,
            default: false,
            description: 'Timpa file existing (processor file di-archive sebelum overwrite)'
        },
        'skip-sql-validation': {
            type: 'boolean',
            required: false,
            default: false,
            description: 'Lewati validasi keyword SQL'
        }
    },
    examples: [
        'npx restforge processor create --project=my-app --name=order-validator --payload=order-validator.json',
        'npx restforge processor create --project=my-app --name=order-validator --payload=order-validator.json --force'
    ],
    async handler(args) {
        try {
            validateSafeName(args.project, 'project');
            validateSafeName(args.name, 'processor');
            const project = ArgumentValidator.validateProjectName(args.project);
            const endpoint = ArgumentValidator.validateEndpointName(args.name);
            const payloadFile = ArgumentValidator.validatePayloadName(args.payload);
            const force = !!args.force;

            console.log('Arguments to process:');
            console.log(`   Project: ${project}`);
            console.log(`   Endpoint: ${endpoint}`);
            console.log(`   Payload: ${payloadFile}`);
            console.log(`   Force overwrite: ${force}`);
            console.log('');
            if (force) {
                console.log('Mode: --force enabled. Endpoint router and processor files will be regenerated.');
                console.log('      Existing processor files will be archived before overwrite.');
            } else {
                console.log('Mode: Endpoint router regenerated. Existing processor files are preserved.');
            }

            const workingDir = getWorkingDirectory();
            const payload = readPayloadFile(payloadFile, workingDir);

            console.log(`\nProcessors to be created:`);
            payload.processor.forEach(proc => {
                console.log(`- ${proc.name}`);
            });
            console.log('');

            if (force) {
                const processorConflicts = ConflictChecker.detectProcessorConflicts(project, endpoint, payload.processor);
                if (processorConflicts.hasConflicts) {
                    console.log('Creating archive files before overwrite...');
                    const archiveFiles = ConflictChecker.createProcessorArchiveFiles(processorConflicts, project, endpoint);
                    if (archiveFiles.length > 0) {
                        console.log(`Successfully archived ${archiveFiles.length} conflicting file(s)`);
                    }
                    console.log('');
                }
            }

            const moduleDir = createOrCheckModule(project, workingDir);
            createEndpointFile(moduleDir, project, endpoint, payload);
            createProcessorFiles(moduleDir, project, endpoint, payload, force);
            updateEndpointMetadata(project, endpoint, payload);
            MetadataManager.updateWorkbenchMetadata(project, endpoint, payload, 'postgres', 'processor');

            console.log('');
            console.log('==========================================');
            console.log('PROCESSOR BERHASIL DIBUAT!');
            console.log('==========================================');
            console.log(`Module: ${project}`);
            console.log(`Endpoint: ${endpoint}`);
            console.log(`Total processors: ${payload.processor.length}`);
            console.log('');
            console.log('Endpoint dapat diakses di:');

            payload.processor.forEach(proc => {
                const hasParams = proc.request && proc.request.params;
                let routePath = `/${proc.name}`;
                if (hasParams) {
                    const paramKeys = Object.keys(proc.request.params);
                    paramKeys.forEach(param => {
                        routePath += `/:${param}`;
                    });
                }
                console.log(`- ${proc.method} http://localhost:3000/api/${project}/${endpoint}${routePath}`);
            });
            console.log('==========================================');
        } catch (error) {
            console.error(`Error: ${error.message}`);
            throw error;
        }
    }
};
