/**
 * Audit Table Migration Generator
 *
 * Menghasilkan SQL DDL untuk audit table sesuai dialect database.
 * Skema audit bersifat fixed dan generic: penambahan/pengurangan field di
 * payload.fieldPolicy TIDAK mengubah struktur tabel karena detail field
 * yang berubah disimpan dalam kolom JSON `changed_fields`.
 *
 * Reference: docs/features/feat-field-policy.md (Section Auto-Creation Audit Table)
 *
 * @module lib/migration/audit-table-generator
 */

const SUPPORTED_DIALECTS = ['postgres', 'mysql', 'oracle'];

/**
 * Generate SQL DDL untuk audit table.
 *
 * @param {string} tableName - Nama tabel sumber (audit table = `${tableName}_audit`)
 * @param {string} dialect - 'postgres' | 'mysql' | 'oracle'
 * @returns {string} SQL DDL siap dieksekusi (idempotent per dialect)
 * @throws {Error} jika dialect tidak didukung atau tableName tidak valid
 */
function generateAuditTableMigration(tableName, dialect) {
  if (!tableName || typeof tableName !== 'string' || tableName.trim() === '') {
    throw new Error('generateAuditTableMigration: tableName must be a non-empty string');
  }
  if (!SUPPORTED_DIALECTS.includes(dialect)) {
    throw new Error(
      `generateAuditTableMigration: unsupported dialect '${dialect}'. ` +
      `Supported: ${SUPPORTED_DIALECTS.join(', ')}`
    );
  }

  const auditTable = `${tableName}_audit`;
  const recordIdx = `idx_${tableName}_audit_record`;
  const timeIdx = `idx_${tableName}_audit_changed_at`;

  if (dialect === 'postgres') {
    return generatePostgresAuditMigration(auditTable, recordIdx, timeIdx);
  }
  if (dialect === 'mysql') {
    return generateMysqlAuditMigration(auditTable, recordIdx, timeIdx);
  }
  return generateOracleAuditMigration(auditTable, recordIdx, timeIdx);
}

function generatePostgresAuditMigration(auditTable, recordIdx, timeIdx) {
  return [
    `CREATE TABLE IF NOT EXISTS ${auditTable} (`,
    `    audit_id        VARCHAR(36) PRIMARY KEY,`,
    `    record_pk       TEXT NOT NULL,`,
    `    operation       VARCHAR(32) NOT NULL,`,
    `    changed_fields  JSONB NOT NULL,`,
    `    metadata        JSONB,`,
    `    changed_by      VARCHAR(128),`,
    `    changed_at      TIMESTAMPTZ NOT NULL DEFAULT NOW()`,
    `);`,
    ``,
    `CREATE INDEX IF NOT EXISTS ${recordIdx}`,
    `    ON ${auditTable} (record_pk, changed_at DESC);`,
    ``,
    `CREATE INDEX IF NOT EXISTS ${timeIdx}`,
    `    ON ${auditTable} (changed_at DESC);`
  ].join('\n');
}

function generateMysqlAuditMigration(auditTable, recordIdx, timeIdx) {
  return [
    `CREATE TABLE IF NOT EXISTS ${auditTable} (`,
    `    audit_id        CHAR(36) NOT NULL,`,
    `    record_pk       VARCHAR(255) NOT NULL,`,
    `    operation       VARCHAR(32) NOT NULL,`,
    `    changed_fields  JSON NOT NULL,`,
    `    metadata        JSON NULL,`,
    `    changed_by      VARCHAR(128) NULL,`,
    `    changed_at      TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,`,
    `    PRIMARY KEY (audit_id),`,
    `    INDEX ${recordIdx} (record_pk, changed_at),`,
    `    INDEX ${timeIdx} (changed_at)`,
    `) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;`
  ].join('\n');
}

function generateOracleAuditMigration(auditTable, recordIdx, timeIdx) {
  // Oracle tidak punya CREATE TABLE IF NOT EXISTS — pakai PL/SQL anonim
  // dengan exception handler ORA-00955 (object name already used) dan
  // ORA-01408 (column already indexed).
  return [
    `BEGIN`,
    `    EXECUTE IMMEDIATE Q'[`,
    `        CREATE TABLE ${auditTable} (`,
    `            audit_id        VARCHAR2(36) PRIMARY KEY,`,
    `            record_pk       VARCHAR2(255) NOT NULL,`,
    `            operation       VARCHAR2(32) NOT NULL,`,
    `            changed_fields  CLOB NOT NULL`,
    `                            CONSTRAINT ${auditTable}_cf_json CHECK (changed_fields IS JSON),`,
    `            metadata        CLOB`,
    `                            CONSTRAINT ${auditTable}_md_json CHECK (metadata IS JSON),`,
    `            changed_by      VARCHAR2(128),`,
    `            changed_at      TIMESTAMP DEFAULT SYSTIMESTAMP NOT NULL`,
    `        )`,
    `    ]';`,
    `EXCEPTION`,
    `    WHEN OTHERS THEN`,
    `        IF SQLCODE != -955 THEN RAISE; END IF;`,
    `END;`,
    `/`,
    ``,
    `BEGIN`,
    `    EXECUTE IMMEDIATE 'CREATE INDEX ${recordIdx} ON ${auditTable} (record_pk, changed_at)';`,
    `EXCEPTION`,
    `    WHEN OTHERS THEN`,
    `        IF SQLCODE != -955 AND SQLCODE != -1408 THEN RAISE; END IF;`,
    `END;`,
    `/`,
    ``,
    `BEGIN`,
    `    EXECUTE IMMEDIATE 'CREATE INDEX ${timeIdx} ON ${auditTable} (changed_at)';`,
    `EXCEPTION`,
    `    WHEN OTHERS THEN`,
    `        IF SQLCODE != -955 AND SQLCODE != -1408 THEN RAISE; END IF;`,
    `END;`,
    `/`
  ].join('\n');
}

module.exports = {
  generateAuditTableMigration,
  SUPPORTED_DIALECTS
};
