/**
 * T003 Step 3a assertion negative-case test.
 *
 * Confirms that widening EXPECTED_PREDICATE to ACCEPTED_PREDICATES (two accepted
 * forms for TEXT vs VARCHAR tenant_id columns) did NOT blunt the assertion —
 * i.e., it still correctly rejects:
 *   (a) NULL polqual  — the original hollow-policy bug
 *   (b) a wrong predicate (USING (true)) — a trivially-permissive policy
 *   (c) an empty string — the ?? "" null-coalescing path
 *
 * And still correctly accepts:
 *   (d) the TEXT form:    (tenant_id = current_tenant_id())
 *   (e) the VARCHAR form: ((tenant_id)::text = current_tenant_id())
 *
 * Method: creates temporary tables, attaches wrong/null policies, queries
 * pg_get_expr(), and runs the ACCEPTED_PREDICATES logic against the result.
 * No existing policies are touched. All objects are cleaned up in finally blocks.
 *
 * Run with:
 *   npx tsx scripts/t003-assertion-negative-test.ts
 *
 * Requires: DATABASE_URL (superuser — needed for CREATE TABLE / CREATE POLICY).
 */

import pg from "pg";

const { Pool } = pg;

if (!process.env.DATABASE_URL) {
  console.error("FATAL: DATABASE_URL is not set.");
  process.exit(1);
}

const pool = new Pool({ connectionString: process.env.DATABASE_URL });

// ── Replicate the exact ACCEPTED_PREDICATES set from server/lib/rls-init.ts ──
// This must be kept in sync with the production set. Any change to the accepted
// set in rls-init.ts must be reflected here.
const ACCEPTED_PREDICATES = new Set([
  "(tenant_id = current_tenant_id())",
  "((tenant_id)::text = current_tenant_id())",
]);

function assertAccepted(label: string, expr: string | null): void {
  const coalesced = expr ?? "";
  const accepted = ACCEPTED_PREDICATES.has(coalesced);
  if (accepted) {
    console.log(`  [PASS] ACCEPTED: ${label} → "${coalesced}"`);
  } else {
    console.log(`  [FAIL] SHOULD_ACCEPT but REJECTED: ${label} → "${coalesced}"`);
    process.exitCode = 1;
  }
}

function assertRejected(label: string, expr: string | null): void {
  const coalesced = expr ?? "";
  const accepted = ACCEPTED_PREDICATES.has(coalesced);
  if (!accepted) {
    console.log(`  [PASS] REJECTED: ${label} → ${expr === null ? "NULL" : `"${coalesced}"`}`);
  } else {
    console.log(`  [FAIL] SHOULD_REJECT but ACCEPTED: ${label} → "${coalesced}"`);
    process.exitCode = 1;
  }
}

async function run(): Promise<void> {
  const client = await pool.connect();

  try {
    console.log("\n=== T003 Step 3a Assertion Negative-Case Test ===\n");

    // ── Part 1: In-memory logic check (no DB needed) ──────────────────────────
    // Verify the ACCEPTED_PREDICATES Set accepts and rejects the right values
    // without touching the database. This is the definitional check.

    console.log("Part 1 — In-memory logic (ACCEPTED_PREDICATES Set):");

    // Acceptance cases
    assertAccepted("TEXT form", "(tenant_id = current_tenant_id())");
    assertAccepted("VARCHAR::text form", "((tenant_id)::text = current_tenant_id())");

    // Rejection cases
    assertRejected("NULL polqual (original hollow-policy bug)", null);
    assertRejected("empty string (null-coalesced NULL)", "");
    assertRejected("USING (true) — trivially permissive", "(true)");
    assertRejected("true — variant without parens", "true");
    assertRejected("wrong column name", "(other_column = current_tenant_id())");
    assertRejected("missing function", "(tenant_id = 'aegis-sovereign')");
    assertRejected("partial match — extra text", "(tenant_id = current_tenant_id()) AND 1=1");

    // ── Part 2: Empirical DB test — wrong predicate (USING (true)) ───────────
    // Creates a temp table, attaches a policy with USING (true), reads
    // pg_get_expr(polqual), and verifies ACCEPTED_PREDICATES rejects it.
    // The temp table is NOT in TENANT_TABLES — no production policy is touched.

    console.log("\nPart 2 — Empirical: USING (true) → pg_get_expr → ACCEPTED_PREDICATES rejects:");

    const wrongTableName = "t003_neg_test_wrong_tmp";
    const wrongPolicyName = `${wrongTableName}_tenant_isolation`;
    try {
      await client.query(`DROP TABLE IF EXISTS ${wrongTableName}`);
      await client.query(
        `CREATE TABLE ${wrongTableName} (id serial PRIMARY KEY, tenant_id text)`,
      );
      await client.query(
        `CREATE POLICY ${wrongPolicyName} ON ${wrongTableName} USING (true)`,
      );

      const { rows } = await client.query<{ using_expr: string | null; withcheck_expr: string | null }>(
        `SELECT pg_get_expr(p.polqual, p.polrelid)      AS using_expr,
                pg_get_expr(p.polwithcheck, p.polrelid) AS withcheck_expr
         FROM   pg_policy p
         JOIN   pg_class  c ON c.oid = p.polrelid
         WHERE  p.polname = $1`,
        [wrongPolicyName],
      );

      if (rows.length === 0) {
        console.log("  [FAIL] Policy not found in pg_policy — cannot test");
        process.exitCode = 1;
      } else {
        const { using_expr, withcheck_expr } = rows[0];
        console.log(`         pg_get_expr(polqual) raw value: "${using_expr}"`);
        console.log(`         pg_get_expr(polwithcheck) raw value: ${withcheck_expr === null ? "NULL" : `"${withcheck_expr}"`}`);
        assertRejected("USING (true) — polqual via pg_get_expr", using_expr);
        // polwithcheck is NULL for a policy with no WITH CHECK clause — also rejected
        assertRejected("WITH CHECK absent (NULL) — polwithcheck via pg_get_expr", withcheck_expr);
      }
    } finally {
      await client.query(`DROP TABLE IF EXISTS ${wrongTableName}`).catch(() => {});
    }

    // ── Part 3: Empirical DB test — NULL polqual (no USING clause) ───────────
    // Creates a temp table, attaches a policy with no USING clause (polqual IS NULL),
    // reads pg_get_expr(polqual), and verifies ACCEPTED_PREDICATES rejects it.
    // A permissive policy with NULL polqual = USING (TRUE) in PostgreSQL; this
    // is the exact state that produced 12/15 FAIL in prod (original bug).

    console.log("\nPart 3 — Empirical: no USING clause → polqual NULL → ACCEPTED_PREDICATES rejects:");

    const nullTableName = "t003_neg_test_null_tmp";
    const nullPolicyName = `${nullTableName}_tenant_isolation`;
    try {
      await client.query(`DROP TABLE IF EXISTS ${nullTableName}`);
      await client.query(
        `CREATE TABLE ${nullTableName} (id serial PRIMARY KEY, tenant_id text)`,
      );
      // Deliberately no USING clause — this is the hollow-policy shape that
      // caused all 69 prod policies to have NULL polqual.
      await client.query(
        `CREATE POLICY ${nullPolicyName} ON ${nullTableName}`,
      );

      const { rows } = await client.query<{ using_expr: string | null }>(
        `SELECT pg_get_expr(p.polqual, p.polrelid) AS using_expr
         FROM   pg_policy p
         JOIN   pg_class  c ON c.oid = p.polrelid
         WHERE  p.polname = $1`,
        [nullPolicyName],
      );

      if (rows.length === 0) {
        console.log("  [FAIL] Policy not found in pg_policy — cannot test");
        process.exitCode = 1;
      } else {
        const { using_expr } = rows[0];
        const isNull = using_expr === null;
        console.log(`         pg_get_expr(polqual) raw value: ${isNull ? "NULL (confirmed)" : `"${using_expr}" — UNEXPECTED`}`);
        if (!isNull) {
          console.log("  [WARN] Expected NULL polqual for policy without USING clause — got non-null; test is less meaningful");
        }
        assertRejected("no USING clause → NULL polqual → ?? '' → empty string", using_expr);
      }
    } finally {
      await client.query(`DROP TABLE IF EXISTS ${nullTableName}`).catch(() => {});
    }

    // ── Part 4: Empirical DB test — correct forms accepted ───────────────────
    // Verifies that a real billing_invoices policy (varchar table → ::text form)
    // is accepted, and a real incident_slas policy (text table → bare form) is
    // accepted. Reads from pg_policy for existing tables — no DDL changes.

    console.log("\nPart 4 — Empirical: real policies → correct forms accepted by ACCEPTED_PREDICATES:");

    const { rows: realPolicies } = await client.query<{
      table_name: string;
      using_expr: string | null;
    }>(
      `SELECT c.relname AS table_name,
              pg_get_expr(p.polqual, p.polrelid) AS using_expr
       FROM   pg_policy p
       JOIN   pg_class  c ON c.oid = p.polrelid
       WHERE  p.polname IN ('billing_invoices_tenant_isolation', 'incident_slas_tenant_isolation')
       ORDER  BY c.relname`,
    );

    if (realPolicies.length === 0) {
      console.log("  [WARN] No real policies found for billing_invoices or incident_slas — skipping acceptance check");
    } else {
      for (const row of realPolicies) {
        assertAccepted(`real policy: ${row.table_name}`, row.using_expr);
      }
    }

    // ── Final verdict ─────────────────────────────────────────────────────────

    const exitCode = process.exitCode ?? 0;
    console.log(`\n=== Negative-case test ${exitCode === 0 ? "PASS" : "FAIL"} ===`);
    if (exitCode === 0) {
      console.log("Step 3a ACCEPTED_PREDICATES set:");
      console.log("  ACCEPTS:  TEXT form and VARCHAR::text form only");
      console.log("  REJECTS:  NULL, empty string, (true), wrong column, partial matches");
      console.log("  → Widening to accept both predicate forms did NOT blunt the assertion.");
    } else {
      console.log("One or more checks failed. Review output above.");
    }
  } finally {
    client.release();
    await pool.end();
  }
}

run().catch((e: any) => {
  console.error("\nFATAL ERROR:", e?.message ?? e);
  process.exit(1);
});
