import type { Dialect } from "@wrnexus/db";

/**
 * DDL for the two assignment tables, as a list of statements rather than one
 * blob: splitting a blob on a separator makes runtime correctness depend on
 * source formatting, and only the sqlite driver accepts multi-statement exec.
 *
 * `scope` holds a tenant id, or the empty string for a global assignment, so
 * the unique constraints work on every dialect (NULL is not comparable in a
 * UNIQUE index). `effect` is CHECK-constrained: an unrecognised value would
 * otherwise be dropped from both the grant and deny buckets on read, silently
 * turning a deny into a no-op.
 */
export function authzMigrationSql(dialect: Dialect): { up: string[]; down: string[] } {
  const id =
    dialect === "postgres"
      ? "SERIAL PRIMARY KEY"
      : dialect === "mysql"
        ? "INT AUTO_INCREMENT PRIMARY KEY"
        : "INTEGER PRIMARY KEY AUTOINCREMENT";
  const timestamp = dialect === "sqlite" ? "TEXT" : "TIMESTAMP";
  // MySQL's default collation is case- and accent-insensitive, which would let
  // tenant "T1" match "t1" and collapse roles "admin"/"Admin" onto one row.
  const exact = dialect === "mysql" ? " COLLATE utf8mb4_bin" : "";
  const key = `VARCHAR(255)${exact} NOT NULL`;

  return {
    up: [
      `CREATE TABLE IF NOT EXISTS _wrn_authz_assignment (
  id ${id},
  subject_id ${key},
  scope ${key} DEFAULT '',
  role ${key},
  created_at ${timestamp} NOT NULL DEFAULT CURRENT_TIMESTAMP,
  CONSTRAINT _wrn_authz_assignment_unique UNIQUE (subject_id, scope, role)
)`,
      `CREATE TABLE IF NOT EXISTS _wrn_authz_grant (
  id ${id},
  subject_id ${key},
  scope ${key} DEFAULT '',
  permission ${key},
  effect VARCHAR(16) NOT NULL CHECK (effect IN ('allow', 'deny')),
  created_at ${timestamp} NOT NULL DEFAULT CURRENT_TIMESTAMP,
  CONSTRAINT _wrn_authz_grant_unique UNIQUE (subject_id, scope, permission)
)`,
    ],
    down: ["DROP TABLE IF EXISTS _wrn_authz_grant", "DROP TABLE IF EXISTS _wrn_authz_assignment"],
  };
}
