/**
 * Pack B — api-key login path re-enable + tenant-isolation proof.
 *
 * Proves the fix to the dead api-key login path (getApiKeyByHash /
 * updateApiKeyLastUsed routed through withBypassRls) WITHOUT going hollow-green:
 *
 *   - RED baseline (run with `pre`): every aegis_ key 401s (lookup dead under
 *     null-GUC RLS).
 *   - GREEN + isolation (run with `post`): the SAME endpoint + SAME ?tenantId=B
 *     query returns 0 rows for the A-key but the B sentinel for the B-key —
 *     i.e. the api-key-derived tenant GUC drives RLS isolation (Rule-18 pair).
 *
 * Proof vehicle: GET /api/i18n/overrides/:locale (routes.ts, NO requireAuth).
 * Its handler reads i18n_overrides (RLS-enrolled) via the request `db` filtered
 * by WHERE locale=? AND tenant_id=<?tenantId query param>. With the explicit
 * WHERE asking for B, a 0-row result for the A-key can ONLY occur because RLS
 * (driven by the A-key's GUC) hid B's rows.
 *
 * Synthetic DEV-ONLY data: two throwaway tenants + deterministic test keys.
 * Run: `tsx scripts/vf-packb-apikey-login-isolation-proof.ts [pre|post]`
 */
import { withBypassRls } from "../server/db";
import { apiKeys, i18nOverrides } from "@shared/schema";
import { hashApiKey } from "../server/lib/api-key-auth";
import { eq, and, inArray } from "drizzle-orm";

const MODE = (process.argv[2] === "pre" ? "pre" : "post") as "pre" | "post";
const BASE = process.env.PACKB_BASE || "http://localhost:5000";

const A = "packb-tenant-a";
const B = "packb-tenant-b";
// Deterministic DEV-ONLY synthetic test keys (NOT secrets; re-derivable across
// the pre/post runs so the same seeded hash matches both).
const RAW_A = "aegis_packb_tenant_a_0000000000000000000000000000000000000000";
const RAW_B = "aegis_packb_tenant_b_1111111111111111111111111111111111111111";
const RAW_INVALID = "aegis_packb_invalid_9999999999999999999999999999999999999999";
const SENTINEL_A = "PACKB_SENTINEL_VALUE_TENANT_A";
const SENTINEL_B = "PACKB_SENTINEL_VALUE_TENANT_B";
const LOCALE = "en";
const KEY = "packb.sentinel";

async function seed() {
  await withBypassRls(async (tx) => {
    await tx.delete(apiKeys).where(inArray(apiKeys.tenantId, [A, B]));
    await tx.delete(i18nOverrides).where(inArray(i18nOverrides.tenantId, [A, B]));
    await tx.insert(apiKeys).values([
      { name: "packb-key-a", keyHash: hashApiKey(RAW_A), keyPrefix: RAW_A.slice(0, 8), tenantId: A, isActive: true },
      { name: "packb-key-b", keyHash: hashApiKey(RAW_B), keyPrefix: RAW_B.slice(0, 8), tenantId: B, isActive: true },
    ]);
    await tx.insert(i18nOverrides).values([
      { tenantId: A, locale: LOCALE, key: KEY, value: SENTINEL_A },
      { tenantId: B, locale: LOCALE, key: KEY, value: SENTINEL_B },
    ]);
  });
}

async function leg(name: string, opts: { key?: string; qTenant: string }) {
  const headers: Record<string, string> = {};
  if (opts.key) headers["x-api-key"] = opts.key;
  const url = `${BASE}/api/i18n/overrides/${LOCALE}?tenantId=${encodeURIComponent(opts.qTenant)}`;
  let status = 0;
  let rows = -1;
  let hasA = false;
  let hasB = false;
  let body = "";
  try {
    const r = await fetch(url, { headers });
    status = r.status;
    body = await r.text();
    try {
      const j = JSON.parse(body);
      if (Array.isArray(j)) {
        rows = j.length;
        hasA = j.some((o: any) => o?.value === SENTINEL_A);
        hasB = j.some((o: any) => o?.value === SENTINEL_B);
      }
    } catch { /* non-JSON body (e.g. error html) */ }
  } catch (e) {
    body = `FETCH_ERROR: ${(e as Error).message}`;
  }
  console.log(
    `  ${name.padEnd(34)} status=${status} rows=${String(rows).padStart(2)} hasA=${hasA} hasB=${hasB}` +
      (status >= 400 ? `  body=${body.slice(0, 120)}` : ""),
  );
  return { status, rows, hasA, hasB };
}

async function readLastUsed() {
  return withBypassRls(async (tx) => {
    const a = (await tx.select().from(apiKeys).where(and(eq(apiKeys.tenantId, A), eq(apiKeys.name, "packb-key-a"))))[0];
    const b = (await tx.select().from(apiKeys).where(and(eq(apiKeys.tenantId, B), eq(apiKeys.name, "packb-key-b"))))[0];
    return { a: a?.lastUsedAt ?? null, b: b?.lastUsedAt ?? null };
  });
}

async function main() {
  console.log(`\n=== Pack B api-key login + isolation proof — MODE=${MODE} — BASE=${BASE} ===`);
  await seed();
  console.log("[seed] 2 tenants (A,B), 1 aegis_ key each, 1 i18n_overrides 'en' sentinel each — done\n");

  console.log("Legs:");
  const L0 = await leg("L0 control no-key      q=B", { qTenant: B });
  const L1 = await leg("L1 flip A-key          q=A", { key: RAW_A, qTenant: A });
  const L2 = await leg("L2 paired-pos B-key    q=B", { key: RAW_B, qTenant: B });
  const L3 = await leg("L3 ISOLATION-neg A-key q=B", { key: RAW_A, qTenant: B });
  const L4 = await leg("L4 ISOLATION-pos B-key q=B", { key: RAW_B, qTenant: B });
  const L5 = await leg("L5 control invalid-key q=A", { key: RAW_INVALID, qTenant: A });

  const lu = await readLastUsed();
  console.log(`\n[last_used_at] keyA=${lu.a ? lu.a.toISOString() : "NULL"}  keyB=${lu.b ? lu.b.toISOString() : "NULL"}`);

  console.log("\nVerdict:");
  if (MODE === "pre") {
    const allKeyed401 = [L1, L2, L3, L4].every((l) => l.status === 401);
    console.log(`  RED baseline: all keyed requests 401 = ${allKeyed401 ? "YES (path dead-but-safe) ✅" : "NO ❌"}`);
  } else {
    const checks: [string, boolean][] = [
      ["L1 flip A-key q=A => 200 + sentinelA, NOT sentinelB", L1.status === 200 && L1.hasA && !L1.hasB],
      ["L2 B-key q=B => 200 + sentinelB, NOT sentinelA", L2.status === 200 && L2.hasB && !L2.hasA],
      ["L3 ISOLATION A-key q=B => 200 + 0 rows (RLS blocks B)", L3.status === 200 && L3.rows === 0 && !L3.hasB],
      ["L4 PAIR B-key q=B => 200 + sentinelB (same query, key differs)", L4.status === 200 && L4.hasB],
      ["L5 invalid key => 401 (fix did not blanket-allow)", L5.status === 401],
      ["L0 no-key q=B => 200 + no B sentinel (default-tenant scope)", L0.status === 200 && !L0.hasB],
      ["last_used_at advanced for both keys (lastUsed bypass works)", !!lu.a && !!lu.b],
    ];
    let pass = true;
    for (const [label, ok] of checks) {
      console.log(`  ${ok ? "PASS" : "FAIL"} — ${label}`);
      if (!ok) pass = false;
    }
    console.log(`\n  OVERALL: ${pass ? "PASS ✅ (re-enabled path isolates by tenant)" : "FAIL ❌"}`);
  }
  process.exit(0);
}

main().catch((e) => {
  console.error(e);
  process.exit(1);
});
