const fs = require('fs');
const path = require('path');
const TestDataManager = require('./test-data-manager');

/**
 * Generator untuk file integration test (.test.js).
 * Menghasilkan file test menggunakan Jest + Supertest
 * yang mengirim HTTP request ke server yang sedang berjalan.
 */
class TestGenerator {

  /**
   * Entry point utama. Generate file test dan update package.json.
   * @param {string} projectName - Nama project
   * @param {string} endpointName - Nama endpoint
   * @param {Object} payload - Payload object dari payload JSON
   * @param {number} port - Port server
   * @param {Object} resolvedData - Hasil resolusi dari TestDataManager.resolveAllFields()
   */
  static createTestFiles(projectName, endpointName, payload, port, resolvedData) {
    const testDir = path.join(process.cwd(), 'tests', 'integration');

    // Buat directory jika belum ada
    if (!fs.existsSync(testDir)) {
      fs.mkdirSync(testDir, { recursive: true });
    }

    const testFilePath = path.join(testDir, `${endpointName}.test.js`);

    // Cek apakah file sudah ada (tanpa --force, dicek di create-test.js)
    const content = this.generateTestContent(projectName, endpointName, payload, port, resolvedData);

    fs.writeFileSync(testFilePath, content, 'utf8');
    console.log(`Created: tests/integration/${endpointName}.test.js`);
  }

  /**
   * Update package.json user project dengan jest, supertest, dan scripts
   * @param {string} endpointName - Nama endpoint
   */
  static updatePackageJson(endpointName) {
    const pkgPath = path.join(process.cwd(), 'package.json');

    if (!fs.existsSync(pkgPath)) {
      console.warn('Warning: package.json not found. Skipping dependency setup.');
      console.log('Run manually: npm install --save-dev jest supertest');
      return;
    }

    try {
      const content = fs.readFileSync(pkgPath, 'utf8');
      const pkg = JSON.parse(content);
      let modified = false;

      // devDependencies
      if (!pkg.devDependencies) pkg.devDependencies = {};
      if (!pkg.devDependencies.jest) {
        pkg.devDependencies.jest = '^29.7.0';
        modified = true;
      }
      if (!pkg.devDependencies.supertest) {
        pkg.devDependencies.supertest = '^6.3.4';
        modified = true;
      }

      // scripts
      if (!pkg.scripts) pkg.scripts = {};
      if (!pkg.scripts.test) {
        pkg.scripts.test = 'jest --testPathPattern=tests/integration --runInBand --forceExit';
        modified = true;
      }
      const testScriptKey = `test:${endpointName}`;
      if (!pkg.scripts[testScriptKey]) {
        pkg.scripts[testScriptKey] = `jest --testPathPattern=${endpointName}.test --runInBand --forceExit`;
        modified = true;
      }

      // jest config
      if (!pkg.jest) {
        pkg.jest = {
          testEnvironment: 'node',
          testTimeout: 30000,
          verbose: true
        };
        modified = true;
      }

      if (modified) {
        fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2), 'utf8');
        console.log('Updated: package.json (jest, supertest, test scripts)');
        console.log('');
        console.log('Remember to run: npm install');
      } else {
        console.log('package.json already configured for testing.');
      }
    } catch (error) {
      console.warn(`Warning: Failed to update package.json: ${error.message}`);
      console.log('Run manually: npm install --save-dev jest supertest');
    }
  }

  // ─── Content Generation ────────────────────────────────────────────────────

  /**
   * Rakit seluruh isi file .test.js
   * @param {string} projectName
   * @param {string} endpointName
   * @param {Object} payload
   * @param {number} port
   * @param {Object} resolvedData - { create, update, warnings }
   * @returns {string}
   */
  static generateTestContent(projectName, endpointName, payload, port, resolvedData) {
    const actions = payload.action || {};
    const primaryKey = payload.primaryKey || 'id';
    const codeField = this.findCodeField(payload);
    const uppercaseFields = this.findUppercaseFields(payload);

    const sections = [];

    // Header
    sections.push(this._generateHeader(projectName, endpointName, port, codeField));

    // beforeAll / afterAll
    if (actions.create && actions.delete) {
      sections.push(this._generateBeforeAll(endpointName, payload, resolvedData.create, codeField));
      sections.push(this._generateAfterAll(primaryKey));
    }

    // Test sections (urutan sesuai standar)
    sections.push(this._generateSection_Health(endpointName));

    if (actions.datatables) {
      sections.push(this._generateSection_Datatables());
    }

    if (actions.create) {
      sections.push(this._generateSection_Create(endpointName, payload, resolvedData.create, codeField, uppercaseFields));
    }

    if (actions.first) {
      sections.push(this._generateSection_First(primaryKey));
    }

    if (actions.update) {
      sections.push(this._generateSection_Update(primaryKey, resolvedData.update));
    }

    if (actions.delete) {
      sections.push(this._generateSection_Delete(primaryKey, codeField));
    }

    if (actions.read) {
      sections.push(this._generateSection_Read());
    }

    if (actions.lookup) {
      sections.push(this._generateSection_LookupDynamic());
      sections.push(this._generateSection_LookupStatic());
    }

    return sections.join('\n\n');
  }

  // ─── Field Analysis ────────────────────────────────────────────────────────

  /**
   * Cari field *_code yang bukan primaryKey
   * @param {Object} payload
   * @returns {string|null}
   */
  static findCodeField(payload) {
    const fieldNames = payload.fieldName || [];
    const primaryKey = payload.primaryKey || '';

    for (const field of fieldNames) {
      if (field === primaryKey) continue;
      if (field.toLowerCase().endsWith('_code') || field.toLowerCase() === 'code') {
        return field;
      }
    }
    return null;
  }

  /**
   * Cari field dengan constraint uppercase: true
   * @param {Object} payload
   * @returns {string[]}
   */
  static findUppercaseFields(payload) {
    const result = [];
    if (!Array.isArray(payload.fieldValidation)) return result;

    for (const field of payload.fieldValidation) {
      if (field.constraints && field.constraints.uppercase) {
        result.push(field.name);
      }
    }
    return result;
  }

  // ─── Section Generators (Private) ──────────────────────────────────────────

  /**
   * Generate header: imports, constants, test data variables
   */
  static _generateHeader(projectName, endpointName, port, codeField) {
    const lines = [];
    lines.push(`const request = require('supertest');`);
    lines.push(``);
    lines.push(`const BASE_URL = 'http://127.0.0.1:${port}';`);
    lines.push(`const API       = '/api/${projectName}/${endpointName}';`);
    lines.push(``);

    if (codeField) {
      lines.push(`// ─── Test Data ────────────────────────────────────────────────────────────────`);
      lines.push(`const TS            = Date.now().toString().slice(-8);`);
      lines.push(`const TEST_CODE     = \`TEST-INT-\${TS}\`;`);
      lines.push(`const TEST_CODE_DUP = \`TEST-DUP-\${TS}\`;`);
      lines.push(``);
    }

    lines.push(`let createdId = null;`);
    lines.push(`let dupId     = null;`);

    return lines.join('\n');
  }

  /**
   * Generate beforeAll block
   */
  static _generateBeforeAll(endpointName, payload, createData, codeField) {
    const primaryKey = payload.primaryKey || 'id';

    // Build send object string
    const sendFields = this._buildSendObject(createData, codeField, '            ');

    const lines = [];
    lines.push(`// ─── Setup & Cleanup ─────────────────────────────────────────────────────────`);
    lines.push(`beforeAll(async () => {`);
    lines.push(`    const res = await request(BASE_URL)`);
    lines.push(`        .post(\`\${API}/create\`)`);
    lines.push(`        .send({`);
    lines.push(sendFields);
    lines.push(`        });`);
    lines.push(``);
    lines.push(`    if (res.status === 201 && res.body.data) {`);
    lines.push(`        createdId = res.body.data.${primaryKey};`);
    lines.push(`    }`);
    lines.push(`});`);

    return lines.join('\n');
  }

  /**
   * Generate afterAll block
   */
  static _generateAfterAll(primaryKey) {
    const lines = [];
    lines.push(`afterAll(async () => {`);
    lines.push(`    if (createdId) {`);
    lines.push(`        await request(BASE_URL)`);
    lines.push(`            .post(\`\${API}/delete\`)`);
    lines.push(`            .send({ where: [{ key: '${primaryKey}', value: createdId }] });`);
    lines.push(`    }`);
    lines.push(`    if (dupId) {`);
    lines.push(`        await request(BASE_URL)`);
    lines.push(`            .post(\`\${API}/delete\`)`);
    lines.push(`            .send({ where: [{ key: '${primaryKey}', value: dupId }] });`);
    lines.push(`    }`);
    lines.push(`});`);

    return lines.join('\n');
  }

  /**
   * Generate section: Health Check
   */
  static _generateSection_Health(endpointName) {
    const lines = [];
    lines.push(`// ── 1. HEALTH CHECK ──────────────────────────────────────────────────────────`);
    lines.push(`describe('GET /health', () => {`);
    lines.push(``);
    lines.push(`    test('mengembalikan status ok', async () => {`);
    lines.push(`        const res = await request(BASE_URL).get(\`\${API}/health\`);`);
    lines.push(``);
    lines.push(`        expect(res.status).toBe(200);`);
    lines.push(`        expect(res.body.status).toBe('ok');`);
    lines.push(`        expect(res.body.endpoint).toBe('${endpointName}');`);
    lines.push(`    });`);
    lines.push(``);
    lines.push(`});`);

    return lines.join('\n');
  }

  /**
   * Generate section: Datatables
   */
  static _generateSection_Datatables() {
    const lines = [];
    lines.push(`// ── 2. DATATABLES ────────────────────────────────────────────────────────────`);
    lines.push(`describe('POST /datatables', () => {`);
    lines.push(``);
    lines.push(`    test('mengembalikan struktur DataTables yang benar', async () => {`);
    lines.push(`        const res = await request(BASE_URL)`);
    lines.push(`            .post(\`\${API}/datatables\`)`);
    lines.push(`            .send({ draw: 1, start: 0, length: 10 });`);
    lines.push(``);
    lines.push(`        expect(res.status).toBe(200);`);
    lines.push(`        expect(res.body).toHaveProperty('draw');`);
    lines.push(`        expect(res.body).toHaveProperty('recordsTotal');`);
    lines.push(`        expect(res.body).toHaveProperty('recordsFiltered');`);
    lines.push(`        expect(res.body).toHaveProperty('data');`);
    lines.push(`        expect(Array.isArray(res.body.data)).toBe(true);`);
    lines.push(`    });`);
    lines.push(``);
    lines.push(`    test('mengembalikan 400 jika payload kosong', async () => {`);
    lines.push(`        const res = await request(BASE_URL)`);
    lines.push(`            .post(\`\${API}/datatables\`)`);
    lines.push(`            .send({});`);
    lines.push(``);
    lines.push(`        expect(res.status).toBe(400);`);
    lines.push(`        expect(res.body.success).toBe(false);`);
    lines.push(`    });`);
    lines.push(``);
    lines.push(`});`);

    return lines.join('\n');
  }

  /**
   * Generate section: Create
   */
  static _generateSection_Create(endpointName, payload, createData, codeField, uppercaseFields) {
    const primaryKey = payload.primaryKey || 'id';
    const lines = [];

    lines.push(`// ── 3. CREATE ─────────────────────────────────────────────────────────────────`);
    lines.push(`describe('POST /create', () => {`);
    lines.push(``);

    // Test 1: verify beforeAll created data
    lines.push(`    test('berhasil membuat data ${endpointName} baru', async () => {`);
    lines.push(`        expect(createdId).not.toBeNull();`);
    lines.push(`        expect(typeof createdId).toBe('string');`);
    lines.push(`    });`);
    lines.push(``);

    // Test 2: response structure
    const structSendFields = this._buildSendObject(createData, codeField, '            ', 'TEST-STRUCT');
    lines.push(`    test('response create memiliki struktur yang benar', async () => {`);
    if (codeField) {
      lines.push(`        const tempCode = \`TEST-STRUCT-\${TS}\`;`);
    }
    lines.push(`        const res = await request(BASE_URL)`);
    lines.push(`            .post(\`\${API}/create\`)`);
    lines.push(`            .send({`);
    lines.push(structSendFields);
    lines.push(`            });`);
    lines.push(``);
    lines.push(`        expect(res.status).toBe(201);`);
    lines.push(`        expect(res.body.success).toBe(true);`);
    lines.push(`        expect(res.body.data).toHaveProperty('${primaryKey}');`);
    if (codeField) {
      lines.push(`        expect(res.body.data).toHaveProperty('${codeField}');`);
    }
    lines.push(``);
    lines.push(`        if (res.body.data?.${primaryKey}) {`);
    lines.push(`            await request(BASE_URL)`);
    lines.push(`                .post(\`\${API}/delete\`)`);
    lines.push(`                .send({ where: [{ key: '${primaryKey}', value: res.body.data.${primaryKey} }] });`);
    lines.push(`        }`);
    lines.push(`    });`);
    lines.push(``);

    // Test 3: uppercase auto-conversion (jika ada field uppercase)
    if (codeField && uppercaseFields.includes(codeField)) {
      const uppercaseSendFields = this._buildSendObject(createData, codeField, '            ', 'test-case');
      lines.push(`    test('${codeField} di-uppercase secara otomatis', async () => {`);
      lines.push(`        const tempCode = \`test-case-\${TS}\`;`);
      lines.push(`        const res = await request(BASE_URL)`);
      lines.push(`            .post(\`\${API}/create\`)`);
      lines.push(`            .send({`);
      lines.push(uppercaseSendFields);
      lines.push(`            });`);
      lines.push(``);
      lines.push(`        expect(res.status).toBe(201);`);
      lines.push(`        expect(res.body.data.${codeField}).toBe(tempCode.toUpperCase());`);
      lines.push(``);
      lines.push(`        if (res.body.data?.${primaryKey}) {`);
      lines.push(`            await request(BASE_URL)`);
      lines.push(`                .post(\`\${API}/delete\`)`);
      lines.push(`                .send({ where: [{ key: '${primaryKey}', value: res.body.data.${primaryKey} }] });`);
      lines.push(`        }`);
      lines.push(`    });`);
      lines.push(``);
    }

    // Test 4: empty payload
    lines.push(`    test('mengembalikan 400 jika payload kosong', async () => {`);
    lines.push(`        const res = await request(BASE_URL).post(\`\${API}/create\`).send({});`);
    lines.push(``);
    lines.push(`        expect(res.status).toBe(400);`);
    lines.push(`        expect(res.body.success).toBe(false);`);
    lines.push(`    });`);
    lines.push(``);

    // Test 5: duplicate (jika ada code field)
    if (codeField) {
      const dupSendFields = this._buildSendObject(createData, codeField, '            ', null, 'TEST_CODE_DUP');
      lines.push(`    test('mengembalikan 409 jika ${codeField} sudah ada (duplicate)', async () => {`);
      lines.push(`        const firstRes = await request(BASE_URL)`);
      lines.push(`            .post(\`\${API}/create\`)`);
      lines.push(`            .send({`);
      lines.push(dupSendFields);
      lines.push(`            });`);
      lines.push(``);
      lines.push(`        if (firstRes.status === 201) {`);
      lines.push(`            dupId = firstRes.body.data?.${primaryKey};`);
      lines.push(`        }`);
      lines.push(``);
      lines.push(`        const dupRes = await request(BASE_URL)`);
      lines.push(`            .post(\`\${API}/create\`)`);
      lines.push(`            .send({`);
      lines.push(dupSendFields);
      lines.push(`            });`);
      lines.push(``);
      lines.push(`        expect(dupRes.status).toBe(409);`);
      lines.push(`        expect(dupRes.body.success).toBe(false);`);
      lines.push(`        expect(dupRes.body.error).toBe('Duplicate entry');`);
      lines.push(`    });`);
      lines.push(``);
    }

    lines.push(`});`);
    return lines.join('\n');
  }

  /**
   * Generate section: First
   */
  static _generateSection_First(primaryKey) {
    const lines = [];
    lines.push(`// ── 4. FIRST ──────────────────────────────────────────────────────────────────`);
    lines.push(`describe('POST /first', () => {`);
    lines.push(``);
    lines.push(`    test('berhasil mengambil data berdasarkan ${primaryKey}', async () => {`);
    lines.push(`        const res = await request(BASE_URL)`);
    lines.push(`            .post(\`\${API}/first\`)`);
    lines.push(`            .send({ where: { key: '${primaryKey}', value: createdId } });`);
    lines.push(``);
    lines.push(`        expect(res.status).toBe(200);`);
    lines.push(`        expect(res.body.success).toBe(true);`);
    lines.push(`        expect(Array.isArray(res.body.data)).toBe(true);`);
    lines.push(`        expect(res.body.data[0].${primaryKey}).toBe(createdId);`);
    lines.push(`    });`);
    lines.push(``);
    lines.push(`    test('mengembalikan 400 jika payload kosong', async () => {`);
    lines.push(`        const res = await request(BASE_URL).post(\`\${API}/first\`).send({});`);
    lines.push(`        expect(res.status).toBe(400);`);
    lines.push(`    });`);
    lines.push(``);
    lines.push(`    test('mengembalikan 400 jika field where tidak valid', async () => {`);
    lines.push(`        const res = await request(BASE_URL)`);
    lines.push(`            .post(\`\${API}/first\`)`);
    lines.push(`            .send({ where: { key: 'kolom_tidak_ada', value: 'test' } });`);
    lines.push(``);
    lines.push(`        expect(res.status).toBe(400);`);
    lines.push(`        expect(res.body.error).toBe('Invalid where field');`);
    lines.push(`    });`);
    lines.push(``);
    lines.push(`});`);

    return lines.join('\n');
  }

  /**
   * Generate section: Update
   */
  static _generateSection_Update(primaryKey, updateData) {
    const updateSendFields = this._buildUpdateSendObject(updateData, primaryKey, '            ');

    const lines = [];
    lines.push(`// ── 5. UPDATE ─────────────────────────────────────────────────────────────────`);
    lines.push(`describe('POST /update', () => {`);
    lines.push(``);
    lines.push(`    test('berhasil mengupdate data', async () => {`);
    lines.push(`        const res = await request(BASE_URL)`);
    lines.push(`            .post(\`\${API}/update\`)`);
    lines.push(`            .send({`);
    lines.push(`                ${primaryKey}: createdId,`);
    lines.push(updateSendFields);
    lines.push(`            });`);
    lines.push(``);
    lines.push(`        expect(res.status).toBe(200);`);
    lines.push(`        expect(res.body.success).toBe(true);`);
    lines.push(`    });`);
    lines.push(``);

    // Verify persistence
    const verifyFields = Object.entries(updateData);
    if (verifyFields.length > 0) {
      lines.push(`    test('perubahan tersimpan di database setelah update', async () => {`);
      lines.push(`        const res = await request(BASE_URL)`);
      lines.push(`            .post(\`\${API}/first\`)`);
      lines.push(`            .send({ where: { key: '${primaryKey}', value: createdId } });`);
      lines.push(``);
      for (const [key, value] of verifyFields) {
        lines.push(`        expect(res.body.data[0].${key}).toBe(${this._formatValue(value)});`);
      }
      lines.push(`    });`);
      lines.push(``);
    }

    lines.push(`    test('mengembalikan 400 jika ${primaryKey} tidak disertakan', async () => {`);
    lines.push(`        const res = await request(BASE_URL)`);
    lines.push(`            .post(\`\${API}/update\`)`);
    lines.push(`            .send({ ${verifyFields.length > 0 ? `${verifyFields[0][0]}: ${this._formatValue(verifyFields[0][1])}` : ''} });`);
    lines.push(``);
    lines.push(`        expect(res.status).toBe(400);`);
    lines.push(`        expect(res.body.error).toBe('Missing required field');`);
    lines.push(`    });`);
    lines.push(``);

    lines.push(`    test('mengembalikan 404 jika ${primaryKey} tidak ditemukan', async () => {`);
    lines.push(`        const res = await request(BASE_URL)`);
    lines.push(`            .post(\`\${API}/update\`)`);
    lines.push(`            .send({ ${primaryKey}: '00000000-0000-0000-0000-000000000000'${verifyFields.length > 0 ? `, ${verifyFields[0][0]}: ${this._formatValue(verifyFields[0][1])}` : ''} });`);
    lines.push(``);
    lines.push(`        expect(res.status).toBe(404);`);
    lines.push(`    });`);
    lines.push(``);
    lines.push(`});`);

    return lines.join('\n');
  }

  /**
   * Generate section: Delete
   */
  static _generateSection_Delete(primaryKey, codeField) {
    const lines = [];
    lines.push(`// ── 6. DELETE ─────────────────────────────────────────────────────────────────`);
    lines.push(`describe('POST /delete', () => {`);
    lines.push(``);
    lines.push(`    test('mengembalikan 404 jika data tidak ditemukan', async () => {`);
    lines.push(`        const res = await request(BASE_URL)`);
    lines.push(`            .post(\`\${API}/delete\`)`);
    lines.push(`            .send({ where: [{ key: '${primaryKey}', value: '00000000-0000-0000-0000-000000000000' }] });`);
    lines.push(``);
    lines.push(`        expect(res.status).toBe(404);`);
    lines.push(`        expect(res.body.error).toBe('Data not found');`);
    lines.push(`    });`);
    lines.push(``);
    lines.push(`    test('berhasil menghapus data', async () => {`);
    lines.push(`        const res = await request(BASE_URL)`);
    lines.push(`            .post(\`\${API}/delete\`)`);
    lines.push(`            .send({ where: [{ key: '${primaryKey}', value: createdId }] });`);
    lines.push(``);
    lines.push(`        expect(res.status).toBe(200);`);
    lines.push(`        expect(res.body.success).toBe(true);`);
    lines.push(``);
    lines.push(`        createdId = null; // afterAll tidak perlu hapus lagi`);
    lines.push(`    });`);
    lines.push(``);

    // Verify deleted
    if (codeField) {
      lines.push(`    test('data benar-benar terhapus dari database', async () => {`);
      lines.push(`        const res = await request(BASE_URL)`);
      lines.push(`            .post(\`\${API}/delete\`)`);
      lines.push(`            .send({ where: [{ key: '${codeField}', value: TEST_CODE }] });`);
      lines.push(``);
      lines.push(`        expect(res.status).toBe(404);`);
      lines.push(`    });`);
      lines.push(``);
    }

    lines.push(`});`);
    return lines.join('\n');
  }

  /**
   * Generate section: Read
   */
  static _generateSection_Read() {
    const lines = [];
    lines.push(`// ── 7. READ ───────────────────────────────────────────────────────────────────`);
    lines.push(`describe('POST /read', () => {`);
    lines.push(``);
    lines.push(`    test('mengembalikan data dengan paginasi', async () => {`);
    lines.push(`        const res = await request(BASE_URL)`);
    lines.push(`            .post(\`\${API}/read\`)`);
    lines.push(`            .send({ page: 1, per_page: 5 });`);
    lines.push(``);
    lines.push(`        expect(res.status).toBe(200);`);
    lines.push(`        expect(res.body).toHaveProperty('pagination');`);
    lines.push(`        expect(res.body.pagination).toHaveProperty('current_page', 1);`);
    lines.push(`        expect(res.body.data.length).toBeLessThanOrEqual(5);`);
    lines.push(`    });`);
    lines.push(``);
    lines.push(`    test('mengembalikan 400 jika payload kosong', async () => {`);
    lines.push(`        const res = await request(BASE_URL).post(\`\${API}/read\`).send({});`);
    lines.push(`        expect(res.status).toBe(400);`);
    lines.push(`    });`);
    lines.push(``);
    lines.push(`    test('mengembalikan 400 jika per_page melebihi 100', async () => {`);
    lines.push(`        const res = await request(BASE_URL)`);
    lines.push(`            .post(\`\${API}/read\`)`);
    lines.push(`            .send({ page: 1, per_page: 200 });`);
    lines.push(``);
    lines.push(`        expect(res.status).toBe(400);`);
    lines.push(`    });`);
    lines.push(``);
    lines.push(`});`);

    return lines.join('\n');
  }

  /**
   * Generate section: Lookup Dynamic (GET)
   */
  static _generateSection_LookupDynamic() {
    const lines = [];
    lines.push(`// ── 8. LOOKUP ─────────────────────────────────────────────────────────────────`);
    lines.push(`describe('GET /lookup (dynamic)', () => {`);
    lines.push(``);
    lines.push(`    test('mengembalikan data lookup dengan X-Request-Mode: dynamic', async () => {`);
    lines.push(`        const res = await request(BASE_URL)`);
    lines.push(`            .get(\`\${API}/lookup\`)`);
    lines.push(`            .set('X-Request-Mode', 'dynamic');`);
    lines.push(``);
    lines.push(`        expect(res.status).toBe(200);`);
    lines.push(`        expect(res.body.success).toBe(true);`);
    lines.push(`        expect(Array.isArray(res.body.data)).toBe(true);`);
    lines.push(`    });`);
    lines.push(``);
    lines.push(`    test('mengembalikan 400 jika X-Request-Mode bukan dynamic', async () => {`);
    lines.push(`        const res = await request(BASE_URL)`);
    lines.push(`            .get(\`\${API}/lookup\`)`);
    lines.push(`            .set('X-Request-Mode', 'static');`);
    lines.push(``);
    lines.push(`        expect(res.status).toBe(400);`);
    lines.push(`    });`);
    lines.push(``);
    lines.push(`});`);

    return lines.join('\n');
  }

  /**
   * Generate section: Lookup Static (POST)
   */
  static _generateSection_LookupStatic() {
    const lines = [];
    lines.push(`describe('POST /lookup (static)', () => {`);
    lines.push(``);
    lines.push(`    test('mengembalikan semua data lookup dengan X-Request-Mode: static', async () => {`);
    lines.push(`        const res = await request(BASE_URL)`);
    lines.push(`            .post(\`\${API}/lookup\`)`);
    lines.push(`            .set('X-Request-Mode', 'static')`);
    lines.push(`            .send({ selected_tag: '' });`);
    lines.push(``);
    lines.push(`        expect(res.status).toBe(200);`);
    lines.push(`        expect(res.body.success).toBe(true);`);
    lines.push(`        expect(Array.isArray(res.body.data)).toBe(true);`);
    lines.push(`    });`);
    lines.push(``);
    lines.push(`    test('mengembalikan 400 jika X-Request-Mode bukan static', async () => {`);
    lines.push(`        const res = await request(BASE_URL)`);
    lines.push(`            .post(\`\${API}/lookup\`)`);
    lines.push(`            .set('X-Request-Mode', 'dynamic')`);
    lines.push(`            .send({ selected_tag: '' });`);
    lines.push(``);
    lines.push(`        expect(res.status).toBe(400);`);
    lines.push(`    });`);
    lines.push(``);
    lines.push(`});`);

    return lines.join('\n');
  }

  // ─── Formatting Helpers ────────────────────────────────────────────────────

  /**
   * Build send object string untuk create payload
   * @param {Object} createData - Resolved create data
   * @param {string|null} codeField - Code field name
   * @param {string} indent - Indentation
   * @param {string|null} codePrefix - Override prefix untuk code field (null = TEST_CODE variable)
   * @param {string|null} codeVariable - Override variable name untuk code field
   * @returns {string}
   */
  static _buildSendObject(createData, codeField, indent, codePrefix, codeVariable) {
    const lines = [];

    for (const [key, value] of Object.entries(createData)) {
      if (key === codeField) {
        if (codeVariable) {
          // Gunakan variable langsung (misal TEST_CODE_DUP)
          lines.push(`${indent}${key}: ${codeVariable},`);
        } else if (codePrefix) {
          // Gunakan template literal dengan prefix custom
          lines.push(`${indent}${key}: \`${codePrefix}-\${TS}\`,`);
        } else {
          // Default: gunakan TEST_CODE variable
          lines.push(`${indent}${key}: TEST_CODE,`);
        }
      } else {
        lines.push(`${indent}${key}: ${this._formatValue(value)},`);
      }
    }

    // Hapus trailing comma dari baris terakhir
    if (lines.length > 0) {
      const lastIdx = lines.length - 1;
      lines[lastIdx] = lines[lastIdx].replace(/,$/, '');
    }

    return lines.join('\n');
  }

  /**
   * Build send object string untuk update payload
   * @param {Object} updateData - Resolved update data
   * @param {string} primaryKey - Primary key field name
   * @param {string} indent - Indentation
   * @returns {string}
   */
  static _buildUpdateSendObject(updateData, primaryKey, indent) {
    const lines = [];

    for (const [key, value] of Object.entries(updateData)) {
      if (key === primaryKey) continue; // primaryKey ditambahkan terpisah sebagai `createdId`
      lines.push(`${indent}${key}: ${this._formatValue(value)},`);
    }

    // Hapus trailing comma dari baris terakhir
    if (lines.length > 0) {
      const lastIdx = lines.length - 1;
      lines[lastIdx] = lines[lastIdx].replace(/,$/, '');
    }

    return lines.join('\n');
  }

  /**
   * Format value sebagai JavaScript literal string
   * @param {*} value
   * @returns {string}
   */
  static _formatValue(value) {
    if (value === null || value === undefined) return 'null';
    if (typeof value === 'string') return `'${value.replace(/'/g, "\\'")}'`;
    if (typeof value === 'boolean') return value.toString();
    if (typeof value === 'number') return value.toString();
    if (Array.isArray(value)) return JSON.stringify(value);
    if (typeof value === 'object') return JSON.stringify(value);
    return String(value);
  }
}

module.exports = TestGenerator;
