/**
 * T004a — model_cards_v2.ownerEmail verification (Tier B, first platform-key (i) table)
 *
 * This is the first proof of the platform-key derivation path:
 *   context = { type: "platform" } — no tenantId, salt = "aegis-pii-platform-aes"
 *
 * Checks:
 *   (a) round-trip: encrypt via platform context → decrypt → original value recovered
 *   (b) ciphertext-on-disk: stored as ENC:v1:; IV randomness across two rows
 *   (c) plaintext passthrough: isEncrypted dual-mode reader leaves non-ENC unchanged
 *   (d) N/A — ownerEmail is notNull; no nullable (i) columns on this table
 *   (e) query-reference: no DB-level filter on ownerEmail; WHERE is on modelKey (structural)
 *   (f) platform-key isolation: ciphertext produced by platform context != tenant context
 *       (proves platform derivation is a distinct key, not accidentally the same as tenant-"")
 *
 * Run: npx tsx scripts/t004a-modelcards-verify.ts
 */

import { withBypassRls } from "../server/db";
import { modelCardsV2 } from "../shared/schema-pilot-extras-10";
import { encryptPii, decryptPii, isEncrypted } from "../server/lib/pii-encryption";
import { eq } from "drizzle-orm";
import { randomBytes } from "crypto";

const PLATFORM_CTX = { type: "platform" as const };
const TENANT_CTX   = { type: "tenant" as const, tenantId: "t004a-mc-verify-tenant" };

const TEST_EMAIL_A  = "ml-ops-verify@aegis-cyber.ug";
const TEST_EMAIL_B  = "fraud-team-verify@aegis-cyber.ug";
const MODEL_KEY_A   = `mc-verify-a-${randomBytes(3).toString("hex")}`;
const MODEL_KEY_B   = `mc-verify-b-${randomBytes(3).toString("hex")}`;
const MODEL_KEY_PFX = "mc-verify-";

let passCount = 0;
let failCount = 0;

function pass(label: string, detail = "") {
  console.log(`  ✅ PASS: ${label}${detail ? " — " + detail : ""}`);
  passCount++;
}
function fail(label: string, detail = "") {
  console.error(`  ❌ FAIL: ${label}${detail ? " — " + detail : ""}`);
  failCount++;
}

async function cleanup() {
  await withBypassRls(async (bdb) => {
    // Remove all rows whose modelKey starts with "mc-verify-"
    const rows = await bdb.select({ id: modelCardsV2.id, modelKey: modelCardsV2.modelKey })
      .from(modelCardsV2);
    const toDelete = rows.filter(r => r.modelKey.startsWith(MODEL_KEY_PFX));
    for (const r of toDelete) {
      await bdb.delete(modelCardsV2).where(eq(modelCardsV2.id, r.id));
    }
  });
}

async function main() {
  console.log("\n╔══════════════════════════════════════════════════════════════╗");
  console.log("║  T004a — model_cards_v2.ownerEmail (Tier B, platform-key)   ║");
  console.log("╚══════════════════════════════════════════════════════════════╝");
  console.log("\n  [NEW PATH] Platform-key derivation — { type: \"platform\" }");
  console.log("  Salt: \"aegis-pii-platform-aes\" | No tenantId anchor\n");

  await cleanup();

  // ── Check (a) + (b): Round-trip + ciphertext-on-disk ──────────────────────
  console.log("--- Check (a)+(b): Round-trip + ciphertext-on-disk ---");

  const [rowA] = await withBypassRls(async (bdb) =>
    bdb.insert(modelCardsV2).values({
      modelKey: MODEL_KEY_A,
      modelName: "Verify Model A", modelVersion: "1.0.0", modelType: "LLM",
      vendor: "Verify Corp",
      purpose: "T004a platform-key verification only.",
      trainingData: "N/A — verification fixture.",
      performanceMetricsJson: { precision: 1.0 },
      limitationsJson: ["verification only"],
      ethicsConsiderationsJson: ["none"],
      driftSignals: "N/A",
      ownerEmail: encryptPii(TEST_EMAIL_A, PLATFORM_CTX),  // T004a (i)
    }).returning()
  );

  if (!rowA) { fail("Row A insert failed"); process.exit(1); }

  // (a) round-trip
  const decEmailA = decryptPii(rowA.ownerEmail, PLATFORM_CTX);
  decEmailA === TEST_EMAIL_A
    ? pass("(a) ownerEmail round-trip", `"${decEmailA}"`)
    : fail("(a) ownerEmail round-trip", `want="${TEST_EMAIL_A}" got="${decEmailA}"`);

  // (b) ciphertext format
  isEncrypted(rowA.ownerEmail)
    ? pass("(b) ownerEmail stored as ENC:v1:", rowA.ownerEmail.slice(0, 20) + "…")
    : fail("(b) ownerEmail stored as ENC:v1:", `raw="${rowA.ownerEmail.slice(0, 40)}"`);

  // (b) varchar length safety: ciphertext must fit within varchar(256)
  rowA.ownerEmail.length <= 256
    ? pass(`(b) ciphertext length within varchar(256)`, `len=${rowA.ownerEmail.length}`)
    : fail(`(b) ciphertext exceeds varchar(256)`, `len=${rowA.ownerEmail.length}`);

  // (b) IV randomness: second row with same email → different ciphertext
  const [rowB] = await withBypassRls(async (bdb) =>
    bdb.insert(modelCardsV2).values({
      modelKey: MODEL_KEY_B,
      modelName: "Verify Model B", modelVersion: "1.0.0", modelType: "Classifier",
      vendor: "Verify Corp",
      purpose: "T004a IV-randomness check fixture.",
      trainingData: "N/A",
      performanceMetricsJson: { accuracy: 1.0 },
      limitationsJson: ["verification only"],
      ethicsConsiderationsJson: ["none"],
      driftSignals: "N/A",
      ownerEmail: encryptPii(TEST_EMAIL_A, PLATFORM_CTX),  // same plaintext as rowA
    }).returning()
  );

  if (rowB) {
    rowA.ownerEmail !== rowB.ownerEmail
      ? pass("(b) IV randomness: same plaintext → different ciphertexts across rows")
      : fail("(b) IV randomness: identical ciphertexts — deterministic encryption (no IV)");
    // (a) round-trip on rowB too
    decryptPii(rowB.ownerEmail, PLATFORM_CTX) === TEST_EMAIL_A
      ? pass("(a) rowB round-trip also correct (same platform key, different IV)")
      : fail("(a) rowB round-trip failed");
  } else {
    fail("(b) rowB insert failed — IV-randomness and rowB round-trip not checked");
  }

  // ── Check (c): Plaintext passthrough ────────────────────────────────────
  console.log("\n--- Check (c): Plaintext passthrough ---");

  const plain = "plain-email@no-enc.ug";
  const passThr = decryptPii(plain, PLATFORM_CTX);
  passThr === plain
    ? pass("(c) non-ENC: plaintext email passed through unchanged")
    : fail("(c) plaintext passthrough failed", `got "${passThr}"`);

  // ── Check (e): Query-reference — no DB filter on ownerEmail ─────────────
  console.log("\n--- Check (e): Query-reference ---");

  const byKey = await withBypassRls(async (bdb) =>
    bdb.select().from(modelCardsV2).where(eq(modelCardsV2.modelKey, MODEL_KEY_A))
  );
  if (byKey.length === 1) {
    const decByKey = decryptPii(byKey[0].ownerEmail, PLATFORM_CTX);
    decByKey === TEST_EMAIL_A
      ? pass("(e) SELECT by modelKey (structural) → decrypted ownerEmail correct")
      : fail("(e) SELECT by modelKey → decrypted ownerEmail wrong", `got "${decByKey}"`);
    isEncrypted(byKey[0].ownerEmail)
      ? pass("(e) ownerEmail remains ENC:v1: on disk when fetched by modelKey")
      : fail("(e) ownerEmail not encrypted on disk");
  } else {
    fail("(e) SELECT by modelKey returned wrong count", `count=${byKey.length}`);
  }

  // ── Check (f): Platform-key isolation ────────────────────────────────────
  // The platform-key ciphertext must differ from a tenant-key ciphertext of the same plaintext.
  // This proves { type: "platform" } derives a distinct key from { type: "tenant", tenantId: "" }
  // and from any real tenant key — the platform path is not accidentally keyed the same.
  console.log("\n--- Check (f): Platform-key isolation ---");

  const encPlatform = encryptPii(TEST_EMAIL_B, PLATFORM_CTX);
  const encTenant   = encryptPii(TEST_EMAIL_B, TENANT_CTX);

  encPlatform !== encTenant
    ? pass("(f) platform ciphertext ≠ tenant ciphertext (distinct key paths)")
    : fail("(f) platform and tenant ciphertexts identical — key derivation not isolated");

  // Decrypt with wrong context → AES-GCM auth tag failure (throw) or wrong plaintext.
  // Either outcome proves the keys are isolated. Throwing is the correct AES-GCM behavior.
  let wrongDecrypt: string | null = null;
  let wrongContextThrew = false;
  try {
    wrongDecrypt = decryptPii(encPlatform, TENANT_CTX);
  } catch {
    wrongContextThrew = true;
  }
  (wrongContextThrew || wrongDecrypt !== TEST_EMAIL_B)
    ? pass("(f) platform ciphertext fails under tenant context (AES-GCM auth tag mismatch or wrong plaintext — keys are isolated)")
    : fail("(f) platform ciphertext decrypted correctly under tenant context — key paths not isolated");

  // Platform key still decrypts its own ciphertext
  const rightDecrypt = decryptPii(encPlatform, PLATFORM_CTX);
  rightDecrypt === TEST_EMAIL_B
    ? pass("(f) platform ciphertext correctly decrypts under platform context")
    : fail("(f) platform ciphertext failed to decrypt under platform context", `got "${rightDecrypt}"`);

  await cleanup();

  // ── Summary ──────────────────────────────────────────────────────────────
  const total = passCount + failCount;
  console.log(`\n${"─".repeat(62)}`);
  console.log(`  Result: ${passCount}/${total} PASS  |  ${failCount} FAIL`);
  if (failCount === 0) {
    console.log("  ✅ ALL PASS — model_cards_v2.ownerEmail platform-key path verified");
    console.log("  Platform-key derivation end-to-end: PROVEN");
  } else {
    console.log("  ❌ FAILURES PRESENT — review output above");
  }
  console.log(`${"─".repeat(62)}\n`);

  process.exit(failCount === 0 ? 0 : 1);
}

main().catch(err => {
  console.error("💥 Unhandled error:", err);
  process.exit(1);
});
