'use strict';

const VALID_DIALECTS = ['postgres', 'mysql', 'oracle', 'sqlite'];

const RE_CREATE_TABLE = /^(\s*)CREATE\s+TABLE\s+/i;
const RE_CREATE_INDEX = /^(\s*)CREATE\s+(UNIQUE\s+)?INDEX\s+/i;
const RE_HAS_IF_NOT_EXISTS = /^\s*CREATE\s+(?:UNIQUE\s+)?(?:TABLE|INDEX)\s+IF\s+NOT\s+EXISTS\b/i;
const RE_DROP = /^\s*DROP\b/i;
const RE_BEGIN = /^\s*BEGIN\b/i;

/**
 * Modify a single SQL statement so the migrate command can run safely under
 * `--drop=false` (statement re-run idempotent).
 *
 * Rules per dialect:
 *   - postgres / sqlite: insert `IF NOT EXISTS ` after `CREATE TABLE ` or
 *     `CREATE INDEX ` (also `CREATE UNIQUE INDEX `).
 *   - mysql: insert `IF NOT EXISTS ` after `CREATE TABLE ` saja. MySQL TIDAK
 *     mendukung `CREATE INDEX IF NOT EXISTS` (syntax error), jadi CREATE INDEX
 *     dibiarkan apa adanya — valid pada first-run; re-run index yang sudah ada
 *     akan gagal duplicate (keterbatasan MySQL, gunakan --drop untuk recreate).
 *   - oracle: wrap CREATE TABLE / CREATE INDEX in a PL/SQL block that suppresses
 *     SQLCODE -955 (ORA-00955: name is already used by an existing object).
 *   - DROP statements pass through unchanged (the emitter already uses IF EXISTS).
 *   - Statements that already include `IF NOT EXISTS` pass through unchanged.
 *   - Statements that are already a PL/SQL block pass through unchanged.
 *
 * @param {string} statement - single statement (output of splitStatements)
 * @param {string} dialect
 * @returns {string} modified statement, or the input when no modification applies
 */
function applyIfNotExistsModifier(statement, dialect) {
  if (typeof statement !== 'string') {
    throw new Error('applyIfNotExistsModifier: statement must be a string');
  }
  if (!VALID_DIALECTS.includes(dialect)) {
    throw new Error(
      `applyIfNotExistsModifier: invalid dialect '${dialect}'. ` +
        `Supported: ${VALID_DIALECTS.join(', ')}`
    );
  }

  if (RE_DROP.test(statement)) return statement;
  if (RE_HAS_IF_NOT_EXISTS.test(statement)) return statement;
  if (RE_BEGIN.test(statement)) return statement;

  const isCreateTable = RE_CREATE_TABLE.test(statement);
  const isCreateIndex = RE_CREATE_INDEX.test(statement);

  if (!isCreateTable && !isCreateIndex) return statement;

  if (dialect === 'oracle') {
    return wrapInPlsqlSuppressDuplicate(statement);
  }

  if (isCreateTable) {
    return statement.replace(RE_CREATE_TABLE, '$1CREATE TABLE IF NOT EXISTS ');
  }

  // MySQL tidak mendukung `CREATE INDEX IF NOT EXISTS` (parser error). Biarkan
  // statement CREATE INDEX apa adanya; valid pada first-run.
  if (dialect === 'mysql') {
    return statement;
  }

  return statement.replace(RE_CREATE_INDEX, (match, leading, unique) => {
    const uniquePart = unique ? 'UNIQUE ' : '';
    return `${leading}CREATE ${uniquePart}INDEX IF NOT EXISTS `;
  });
}

function wrapInPlsqlSuppressDuplicate(statement) {
  const escaped = statement.replace(/'/g, "''");
  return [
    'BEGIN',
    `    EXECUTE IMMEDIATE '${escaped}';`,
    'EXCEPTION',
    '    WHEN OTHERS THEN',
    '        IF SQLCODE != -955 THEN RAISE; END IF;',
    'END;'
  ].join('\n');
}

module.exports = { applyIfNotExistsModifier };
