import type { Migration } from "./migrate.ts";

export interface MigrationSafetyIssue {
  code:
    | "WRN-DB-DROP-TABLE"
    | "WRN-DB-DROP-COLUMN"
    | "WRN-DB-RENAME"
    | "WRN-DB-TYPE-CHANGE"
    | "WRN-DB-SET-NOT-NULL"
    | "WRN-DB-ADD-REQUIRED"
    | "WRN-DB-BLOCKING-INDEX";
  severity: "error" | "warning";
  migration: string;
  statement: string;
  recommendation: string;
}

function statements(sql: string): string[] {
  return sql
    .replace(/\/\*[\s\S]*?\*\//g, " ")
    .split(";")
    .map((statement) =>
      statement
        .replace(/--[^\r\n]*/g, " ")
        .replace(/\s+/g, " ")
        .trim(),
    )
    .filter(Boolean);
}

export function analyzeMigrationSafety(migration: Migration): MigrationSafetyIssue[] {
  const issues: MigrationSafetyIssue[] = [];
  const add = (
    code: MigrationSafetyIssue["code"],
    severity: MigrationSafetyIssue["severity"],
    statement: string,
    recommendation: string,
  ): void => {
    issues.push({ code, severity, migration: migration.name, statement, recommendation });
  };
  for (const statement of statements(migration.up)) {
    if (/\bDROP\s+TABLE\b/i.test(statement))
      add(
        "WRN-DB-DROP-TABLE",
        "error",
        statement,
        "Deprecate reads/writes first; drop in a later contract release.",
      );
    if (
      /\bDROP\s+(?:COLUMN\s+)?[A-Za-z_][\w$]*/i.test(statement) &&
      /\bALTER\s+TABLE\b/i.test(statement)
    )
      add(
        "WRN-DB-DROP-COLUMN",
        "error",
        statement,
        "Stop all old-version reads before a separate contract migration.",
      );
    if (/\bRENAME\s+(?:COLUMN\s+)?\b|\bRENAME\s+TO\b/i.test(statement))
      add(
        "WRN-DB-RENAME",
        "error",
        statement,
        "Add the new name, dual-write/backfill, switch readers, then remove the old name.",
      );
    if (
      /\bALTER\s+(?:COLUMN\s+)?[A-Za-z_][\w$]*\s+(?:TYPE|SET\s+DATA\s+TYPE)\b|\bMODIFY\s+(?:COLUMN\s+)?[A-Za-z_]/i.test(
        statement,
      )
    )
      add(
        "WRN-DB-TYPE-CHANGE",
        "error",
        statement,
        "Add a compatible column and backfill before switching readers.",
      );
    if (/\bALTER\s+(?:COLUMN\s+)?[A-Za-z_][\w$]*\s+SET\s+NOT\s+NULL\b/i.test(statement))
      add(
        "WRN-DB-SET-NOT-NULL",
        "error",
        statement,
        "Backfill and validate existing rows before enforcing NOT NULL.",
      );
    if (
      /\bADD\s+(?:COLUMN\s+)?[A-Za-z_][\w$]*[\s\S]*\bNOT\s+NULL\b/i.test(statement) &&
      !/\bDEFAULT\b/i.test(statement)
    )
      add(
        "WRN-DB-ADD-REQUIRED",
        "error",
        statement,
        "Add nullable, backfill in batches, then enforce the constraint.",
      );
    if (/^CREATE\s+(?:UNIQUE\s+)?INDEX\b/i.test(statement) && !/\bCONCURRENTLY\b/i.test(statement))
      add(
        "WRN-DB-BLOCKING-INDEX",
        "warning",
        statement,
        "Use an online/concurrent index operation when the database supports it.",
      );
  }
  return issues;
}

export function analyzeMigrations(migrations: readonly Migration[]): MigrationSafetyIssue[] {
  return migrations.flatMap(analyzeMigrationSafety);
}
