/**
 * CBS Gateway Integration Test Harness
 *
 * Tests the AEGIS CBS Gateway against Finacle / FlexCube / T24 mock endpoints.
 * Covers: throughput, failure-mode, and audit-trail validation per the
 * BUILD_PRIORITY_LIST P5-deferred item (CBS Gateway integration test harness).
 *
 * Run: npx tsx scripts/cbs-gateway-harness.ts
 *
 * No database connection required. No environment variables required.
 * The gateway is an in-memory simulation — this harness is pure test scaffolding.
 *
 * Categories (10):
 *   CAT-1  Provider configuration validation (3 tests)
 *   CAT-2  Account operations — per provider (9 tests)
 *   CAT-3  Transaction operations — per provider (6 tests)
 *   CAT-4  Two-phase commit audit trail (4 tests)
 *   CAT-5  Kill-switch lifecycle (5 tests)
 *   CAT-6  Emergency batch freeze (3 tests)
 *   CAT-7  Throughput — concurrent operations (2 tests)
 *   CAT-8  Failure-mode — prove-it-fails (5 tests)   ← mandatory negative controls
 *   CAT-9  Audit-trail — transaction log integrity (4 tests)
 *   CAT-10 Health check — per provider (4 tests)
 *
 * Prove-it-fails discipline (advisor condition): the harness must CATCH failure
 * modes, not just pass through them. CAT-8 tests are negative controls; if the
 * gateway incorrectly returns SUCCESS on a failure path, the test FAILS.
 * A harness that passes everything regardless tests nothing.
 */

import { coreBankingGateway, CBSProvider } from "../server/lib/core-banking-gateway";

// ─── Result tracking ─────────────────────────────────────────────────────────

interface TestResult {
  category: string;
  name: string;
  passed: boolean;
  detail?: string;
}

const results: TestResult[] = [];

function pass(category: string, name: string, detail?: string): void {
  results.push({ category, name, passed: true, detail });
  console.log(`  ✅  PASS  ${name}${detail ? ` — ${detail}` : ""}`);
}

function fail(category: string, name: string, detail: string): void {
  results.push({ category, name, passed: false, detail });
  console.log(`  ❌  FAIL  ${name} — ${detail}`);
}

function section(title: string): void {
  console.log(`\n── ${title} ${"─".repeat(Math.max(0, 60 - title.length))}`);
}

// ─── CAT-1: Provider configuration validation ────────────────────────────────

async function runCat1(): Promise<void> {
  section("CAT-1  Provider configuration validation");

  const providers: CBSProvider[] = ["finacle", "flexcube", "t24"];

  for (const provider of providers) {
    const health = await coreBankingGateway.checkCBSHealth(provider);
    if (health.lastCheck instanceof Date && typeof health.latency === "number") {
      pass("CAT-1", `${provider} health check returns structured response`, `latency=${health.latency}ms`);
    } else {
      fail("CAT-1", `${provider} health check`, "response missing lastCheck or latency fields");
    }
  }
}

// ─── CAT-2: Account operations — per provider ────────────────────────────────

async function runCat2(): Promise<void> {
  section("CAT-2  Account operations — per provider");

  const providers: CBSProvider[] = ["finacle", "flexcube", "t24"];
  const testAccount = "ACC-HARNESS-TEST-001";

  for (const provider of providers) {
    // Freeze
    const freezeResult = await coreBankingGateway.freezeAccount(
      testAccount,
      "Harness: suspicious activity test",
      "HARNESS-AGENT",
      provider
    );
    if (
      freezeResult.status === "SUCCESS" &&
      typeof freezeResult.cbsReference === "string" &&
      freezeResult.cbsReference.startsWith(provider.toUpperCase()) &&
      typeof freezeResult.transactionId === "string" &&
      freezeResult.transactionId.startsWith("CBS-TX-")
    ) {
      pass("CAT-2", `${provider} freezeAccount`, `cbsRef=${freezeResult.cbsReference}`);
    } else {
      fail("CAT-2", `${provider} freezeAccount`, `status=${freezeResult.status} error=${freezeResult.error}`);
    }

    // Unfreeze
    const unfreezeResult = await coreBankingGateway.unfreezeAccount(
      testAccount,
      "Harness: test release",
      "HARNESS-AGENT",
      provider
    );
    if (unfreezeResult.status === "SUCCESS" && unfreezeResult.cbsReference?.includes("UNFREEZE")) {
      pass("CAT-2", `${provider} unfreezeAccount`, `cbsRef=${unfreezeResult.cbsReference}`);
    } else {
      fail("CAT-2", `${provider} unfreezeAccount`, `status=${unfreezeResult.status} error=${unfreezeResult.error}`);
    }

    // PND hold
    const pndResult = await coreBankingGateway.postNoDebitHold(
      testAccount,
      "Harness: PND hold test",
      "HARNESS-AGENT",
      provider
    );
    if (
      pndResult.status === "SUCCESS" &&
      (pndResult.cbsReference?.includes("PND") ?? false)
    ) {
      pass("CAT-2", `${provider} postNoDebitHold`, `cbsRef=${pndResult.cbsReference}`);
    } else {
      fail("CAT-2", `${provider} postNoDebitHold`, `status=${pndResult.status} error=${pndResult.error}`);
    }
  }
}

// ─── CAT-3: Transaction operations — per provider ────────────────────────────

async function runCat3(): Promise<void> {
  section("CAT-3  Transaction operations — per provider");

  const providers: CBSProvider[] = ["finacle", "flexcube", "t24"];
  const testTxId = "TXN-HARNESS-TEST-001";

  for (const provider of providers) {
    // Flag transaction
    const flagResult = await coreBankingGateway.flagTransaction(
      testTxId,
      "REVIEW",
      "Harness: suspicious pattern",
      "HARNESS-AGENT",
      provider
    );
    if (
      flagResult.status === "SUCCESS" &&
      flagResult.data?.flag === "REVIEW" &&
      flagResult.data?.requiresMakerChecker === true
    ) {
      pass("CAT-3", `${provider} flagTransaction REVIEW`, `cbsRef=${flagResult.cbsReference}`);
    } else {
      fail("CAT-3", `${provider} flagTransaction`, `status=${flagResult.status} data=${JSON.stringify(flagResult.data)}`);
    }

    // Reverse transaction
    const reverseResult = await coreBankingGateway.reverseTransaction(
      testTxId,
      "Harness: test reversal",
      "HARNESS-AGENT",
      provider
    );
    if (
      reverseResult.status === "SUCCESS" &&
      reverseResult.data?.originalTxId === testTxId &&
      typeof reverseResult.data?.reversalReference === "string"
    ) {
      pass("CAT-3", `${provider} reverseTransaction`, `reversalRef=${reverseResult.data.reversalReference}`);
    } else {
      fail("CAT-3", `${provider} reverseTransaction`, `status=${reverseResult.status} error=${reverseResult.error}`);
    }
  }
}

// ─── CAT-4: Two-phase commit audit trail ─────────────────────────────────────

async function runCat4(): Promise<void> {
  section("CAT-4  Two-phase commit audit trail");

  const freezeResult = await coreBankingGateway.freezeAccount(
    "ACC-HARNESS-2PC-001",
    "Harness: 2PC audit trail test",
    "HARNESS-AGENT",
    "finacle"
  );

  if (freezeResult.status !== "SUCCESS") {
    fail("CAT-4", "2PC freeze precondition", `Expected SUCCESS, got ${freezeResult.status}`);
    fail("CAT-4", "2PC log retrievable", "Precondition failed — freeze did not succeed");
    fail("CAT-4", "2PC log phase=commit", "Precondition failed");
    fail("CAT-4", "2PC log operations populated", "Precondition failed");
    return;
  }

  const txLog = coreBankingGateway.getTransactionLog(freezeResult.transactionId);

  if (txLog !== undefined) {
    pass("CAT-4", "2PC log retrievable by transactionId", `id=${freezeResult.transactionId}`);
  } else {
    fail("CAT-4", "2PC log retrievable by transactionId", "getTransactionLog returned undefined after successful freeze");
    return;
  }

  if (txLog.phase === "commit" && txLog.overallStatus === "committed") {
    pass("CAT-4", "2PC log phase=commit, overallStatus=committed", `phase=${txLog.phase}`);
  } else {
    fail("CAT-4", "2PC log phase/overallStatus", `phase=${txLog.phase} status=${txLog.overallStatus}`);
  }

  const aegisOp = txLog.operations.find(op => op.system === "aegis" && op.status === "success");
  const cbsOp = txLog.operations.find(op => op.system === "cbs" && op.status === "success");

  if (aegisOp && cbsOp) {
    pass("CAT-4", "2PC log has aegis + cbs operations both success", `ops=${txLog.operations.length}`);
  } else {
    fail("CAT-4", "2PC log operations", `aegis=${!!aegisOp} cbs=${!!cbsOp} total=${txLog.operations.length}`);
  }

  const recentLogs = coreBankingGateway.getRecentTransactionLogs(10);
  const found = recentLogs.some(log => log.id === freezeResult.transactionId);
  if (found && recentLogs.length > 0) {
    pass("CAT-4", "getRecentTransactionLogs includes current transaction", `total logs returned=${recentLogs.length}`);
  } else {
    fail("CAT-4", "getRecentTransactionLogs", `found=${found} totalLogs=${recentLogs.length}`);
  }
}

// ─── CAT-5: Kill-switch lifecycle ────────────────────────────────────────────

async function runCat5(): Promise<void> {
  section("CAT-5  Kill-switch lifecycle");

  const testAccount = "ACC-HARNESS-KS-001";

  // Freeze to create a kill switch
  const freezeResult = await coreBankingGateway.freezeAccount(
    testAccount,
    "Harness: kill-switch lifecycle test",
    "HARNESS-AGENT",
    "finacle"
  );

  if (freezeResult.status !== "SUCCESS") {
    fail("CAT-5", "Kill-switch creation precondition", `Freeze failed: ${freezeResult.error}`);
    return;
  }

  // Find the created kill switch
  const activeKillSwitches = coreBankingGateway.getActiveKillSwitches();
  const ks = activeKillSwitches.find(k => k.target === testAccount && k.type === "account_freeze");

  if (ks) {
    pass("CAT-5", "getActiveKillSwitches includes freeze-generated kill switch", `id=${ks.id} target=${ks.target}`);
  } else {
    fail("CAT-5", "getActiveKillSwitches after freeze", "kill switch for test account not found in active list");
    return;
  }

  // Verify kill-switch fields
  if (
    ks.status === "active" &&
    ks.initiatedBy === "HARNESS-AGENT" &&
    typeof ks.cbsReference === "string" &&
    typeof ks.aegisReference === "string"
  ) {
    pass("CAT-5", "Kill-switch fields populated correctly", `aegisRef=${ks.aegisReference}`);
  } else {
    fail("CAT-5", "Kill-switch fields", `status=${ks.status} cbsRef=${ks.cbsReference} aegisRef=${ks.aegisReference}`);
  }

  // Release
  const releaseResult = await coreBankingGateway.releaseKillSwitch(ks.id, "HARNESS-AGENT", "Harness: test release");

  if (releaseResult.success === true) {
    pass("CAT-5", "releaseKillSwitch succeeds", `id=${ks.id}`);
  } else {
    fail("CAT-5", "releaseKillSwitch", `success=${releaseResult.success} error=${releaseResult.error}`);
    return;
  }

  // Verify no longer active
  const activeAfterRelease = coreBankingGateway.getActiveKillSwitches();
  const stillActive = activeAfterRelease.find(k => k.id === ks.id);

  if (!stillActive) {
    pass("CAT-5", "Released kill-switch no longer in active list", `id=${ks.id}`);
  } else {
    fail("CAT-5", "Kill-switch status after release", `Still appears active: id=${ks.id}`);
  }
}

// ─── CAT-6: Emergency batch freeze ───────────────────────────────────────────

async function runCat6(): Promise<void> {
  section("CAT-6  Emergency batch freeze");

  const accountIds = ["ACC-BATCH-001", "ACC-BATCH-002", "ACC-BATCH-003"];
  const result = await coreBankingGateway.activateEmergencyFreeze(
    accountIds,
    "Harness: emergency freeze test",
    "HARNESS-AGENT",
    "finacle"
  );

  if (result.success === accountIds.length && result.failed === 0) {
    pass("CAT-6", `Emergency batch freeze — ${accountIds.length}/${accountIds.length} accounts succeeded`, `success=${result.success} failed=${result.failed}`);
  } else {
    fail("CAT-6", "Emergency batch freeze success count", `success=${result.success} failed=${result.failed} expected=${accountIds.length}`);
  }

  const allHaveRefs = result.results.every(r => r.status === "SUCCESS" && typeof r.cbsReference === "string");
  if (allHaveRefs) {
    pass("CAT-6", "All batch results carry cbsReference", `count=${result.results.length}`);
  } else {
    const missing = result.results.filter(r => r.status !== "SUCCESS" || typeof r.cbsReference !== "string");
    fail("CAT-6", "Batch cbsReference population", `${missing.length} results missing reference or not SUCCESS`);
  }

  const stats = coreBankingGateway.getStats();
  if (typeof stats.activeKillSwitches === "number" && typeof stats.totalKillSwitches === "number") {
    pass("CAT-6", "getStats returns numeric kill-switch counts post-batch", `active=${stats.activeKillSwitches} total=${stats.totalKillSwitches}`);
  } else {
    fail("CAT-6", "getStats", `activeKillSwitches=${stats.activeKillSwitches} totalKillSwitches=${stats.totalKillSwitches}`);
  }
}

// ─── CAT-7: Throughput — concurrent operations ───────────────────────────────

async function runCat7(): Promise<void> {
  section("CAT-7  Throughput — concurrent operations");

  const CONCURRENT_OPS = 20;
  const WALL_CLOCK_LIMIT_MS = 6000;

  const start = Date.now();

  const ops = Array.from({ length: CONCURRENT_OPS }, (_, i) =>
    coreBankingGateway.freezeAccount(
      `ACC-THROUGHPUT-${String(i).padStart(3, "0")}`,
      "Harness: throughput test",
      "HARNESS-AGENT",
      i % 2 === 0 ? "finacle" : "flexcube"
    )
  );

  const allResults = await Promise.all(ops);
  const elapsed = Date.now() - start;

  const allSucceeded = allResults.every(r => r.status === "SUCCESS");
  if (allSucceeded) {
    pass("CAT-7", `${CONCURRENT_OPS} concurrent freezeAccount — all SUCCESS`, `elapsed=${elapsed}ms`);
  } else {
    const failed = allResults.filter(r => r.status !== "SUCCESS").length;
    fail("CAT-7", `${CONCURRENT_OPS} concurrent freezeAccount`, `${failed} failed out of ${CONCURRENT_OPS}`);
  }

  if (elapsed < WALL_CLOCK_LIMIT_MS) {
    pass("CAT-7", `${CONCURRENT_OPS} concurrent ops completed within ${WALL_CLOCK_LIMIT_MS}ms`, `elapsed=${elapsed}ms`);
  } else {
    fail("CAT-7", "Throughput wall-clock limit", `elapsed=${elapsed}ms exceeded limit=${WALL_CLOCK_LIMIT_MS}ms`);
  }
}

// ─── CAT-8: Failure-mode — prove-it-fails (NEGATIVE CONTROLS) ───────────────
//
// These tests MUST receive failure responses. If any returns SUCCESS, the test
// FAILS — a gateway that succeeds on failure paths tests nothing. Per advisor
// condition: "prove-it-fails discipline — demonstrate the harness catches the
// failure modes it is built to catch."
//
async function runCat8(): Promise<void> {
  section("CAT-8  Failure-mode — negative controls (prove-it-fails)");

  // NC-1: Unknown provider → FAILED (not SUCCESS)
  const unknownProviderResult = await coreBankingGateway.freezeAccount(
    "ACC-NC-001",
    "Harness: unknown provider test",
    "HARNESS-AGENT",
    "unknown_bank" as CBSProvider
  );
  if (unknownProviderResult.status === "FAILED" && unknownProviderResult.error?.includes("Unknown CBS provider")) {
    pass("CAT-8", "NC-1: unknown provider → FAILED with correct error message", `error="${unknownProviderResult.error}"`);
  } else {
    fail("CAT-8", "NC-1: unknown provider should return FAILED", `Got status=${unknownProviderResult.status} error=${unknownProviderResult.error}`);
  }

  // NC-2: Unknown provider on transaction flag → FAILED
  const unknownFlagResult = await coreBankingGateway.flagTransaction(
    "TXN-NC-001",
    "BLOCK",
    "Harness: unknown provider tx test",
    "HARNESS-AGENT",
    "unknown_bank" as CBSProvider
  );
  if (unknownFlagResult.status === "FAILED") {
    pass("CAT-8", "NC-2: unknown provider flagTransaction → FAILED", `error="${unknownFlagResult.error}"`);
  } else {
    fail("CAT-8", "NC-2: unknown provider flagTransaction", `Expected FAILED, got ${unknownFlagResult.status}`);
  }

  // NC-3: Release a non-existent kill switch → failure
  const noSuchKs = await coreBankingGateway.releaseKillSwitch(
    "KS-DOES-NOT-EXIST-99999",
    "HARNESS-AGENT"
  );
  if (noSuchKs.success === false && typeof noSuchKs.error === "string") {
    pass("CAT-8", "NC-3: release non-existent kill switch → failure with error", `error="${noSuchKs.error}"`);
  } else {
    fail("CAT-8", "NC-3: release non-existent kill switch", `Expected success=false, got success=${noSuchKs.success}`);
  }

  // NC-4: Release an already-released kill switch → failure (not double-release)
  // First create + release a real kill switch
  const freezeForNc4 = await coreBankingGateway.freezeAccount(
    "ACC-NC-004",
    "Harness: NC-4 setup",
    "HARNESS-AGENT",
    "finacle"
  );
  const activeList = coreBankingGateway.getActiveKillSwitches();
  const ksForNc4 = activeList.find(k => k.target === "ACC-NC-004" && k.type === "account_freeze");

  if (!ksForNc4) {
    fail("CAT-8", "NC-4: double-release precondition", `Kill switch not found after freeze (freeze status: ${freezeForNc4.status})`);
  } else {
    await coreBankingGateway.releaseKillSwitch(ksForNc4.id, "HARNESS-AGENT", "NC-4 first release");
    const secondRelease = await coreBankingGateway.releaseKillSwitch(ksForNc4.id, "HARNESS-AGENT", "NC-4 second release");
    if (secondRelease.success === false && typeof secondRelease.error === "string") {
      pass("CAT-8", "NC-4: double-release → failure on second attempt", `error="${secondRelease.error}"`);
    } else {
      fail("CAT-8", "NC-4: double-release", `Expected success=false on second release, got success=${secondRelease.success}`);
    }
  }

  // NC-5: Verify harness would CATCH an incorrect SUCCESS — meta-test
  // If NC-1 had incorrectly returned SUCCESS, the harness would have FAILed that test.
  // This probe verifies the harness logic itself: it receives a KNOWN-FAILED operation
  // result and verifies it is classified correctly.
  const knownFailed = unknownProviderResult; // already retrieved above
  const harnessDetectsFailure = knownFailed.status === "FAILED";
  if (harnessDetectsFailure) {
    pass("CAT-8", "NC-5: harness correctly classifies FAILED status — meta-verification", `harness would have caught a false SUCCESS`);
  } else {
    fail("CAT-8", "NC-5: harness meta-verification", "Harness classification logic is broken — this should never happen");
  }
}

// ─── CAT-9: Audit-trail — transaction log integrity ──────────────────────────

async function runCat9(): Promise<void> {
  section("CAT-9  Audit-trail — transaction log integrity");

  const result1 = await coreBankingGateway.freezeAccount(
    "ACC-AUDITTRAIL-001",
    "Harness: audit trail test A",
    "HARNESS-AUDIT-AGENT",
    "t24"
  );
  const result2 = await coreBankingGateway.flagTransaction(
    "TXN-AUDITTRAIL-001",
    "HOLD",
    "Harness: audit trail test B",
    "HARNESS-AUDIT-AGENT",
    "t24"
  );

  // Verify transaction IDs are unique
  if (result1.transactionId !== result2.transactionId) {
    pass("CAT-9", "Transaction IDs are unique across distinct operations", `id1=${result1.transactionId} id2=${result2.transactionId}`);
  } else {
    fail("CAT-9", "Transaction ID uniqueness", `Both operations produced same transactionId=${result1.transactionId}`);
  }

  // Verify timestamp is a real Date
  if (result1.timestamp instanceof Date && !isNaN(result1.timestamp.getTime())) {
    pass("CAT-9", "Response timestamp is a valid Date", `ts=${result1.timestamp.toISOString()}`);
  } else {
    fail("CAT-9", "Response timestamp", `timestamp=${result1.timestamp} is not a valid Date`);
  }

  // Verify transaction log has correct initiatedBy
  const txLog = coreBankingGateway.getTransactionLog(result1.transactionId);
  if (txLog?.initiatedBy === "HARNESS-AUDIT-AGENT") {
    pass("CAT-9", "Transaction log preserves initiatedBy in audit record", `initiatedBy=${txLog.initiatedBy}`);
  } else {
    fail("CAT-9", "Transaction log initiatedBy", `expected="HARNESS-AUDIT-AGENT" got="${txLog?.initiatedBy}"`);
  }

  // Verify stats counts include t24 transactions
  const stats = coreBankingGateway.getStats();
  if (stats.cbsProviders >= 3) {
    pass("CAT-9", "Stats report correct provider count (≥3)", `cbsProviders=${stats.cbsProviders}`);
  } else {
    fail("CAT-9", "Stats cbsProviders", `expected ≥3, got ${stats.cbsProviders}`);
  }
}

// ─── CAT-10: Health check — all providers ────────────────────────────────────

async function runCat10(): Promise<void> {
  section("CAT-10  Health check — all providers");

  const allHealth = await coreBankingGateway.getAllCBSHealth();
  const providers: CBSProvider[] = ["finacle", "flexcube", "t24"];

  for (const provider of providers) {
    const h = allHealth[provider];
    if (h && typeof h.healthy === "boolean" && typeof h.latency === "number") {
      pass("CAT-10", `${provider} health entry present in getAllCBSHealth`, `healthy=${h.healthy} latency=${h.latency}ms`);
    } else {
      fail("CAT-10", `${provider} health entry`, `entry=${JSON.stringify(h)}`);
    }
  }

  // Verify all three providers are present in the health map
  const keys = Object.keys(allHealth);
  if (providers.every(p => keys.includes(p))) {
    pass("CAT-10", "getAllCBSHealth covers all three providers", `keys=[${keys.join(", ")}]`);
  } else {
    fail("CAT-10", "getAllCBSHealth provider coverage", `missing providers; got keys=[${keys.join(", ")}]`);
  }
}

// ─── Main runner ─────────────────────────────────────────────────────────────

async function main(): Promise<void> {
  console.log("CBS Gateway Integration Test Harness");
  console.log("=====================================");
  console.log("Finacle / FlexCube / T24 mock-CBS — throughput + failure-mode + audit-trail");
  console.log(`Run started: ${new Date().toISOString()}\n`);

  await runCat1();
  await runCat2();
  await runCat3();
  await runCat4();
  await runCat5();
  await runCat6();
  await runCat7();
  await runCat8();
  await runCat9();
  await runCat10();

  // ─── Summary ───────────────────────────────────────────────────────────────

  console.log("\n\n═══════════════════════════════════════════════════════════════");
  console.log("  SUMMARY");
  console.log("═══════════════════════════════════════════════════════════════");

  const categories = [...new Set(results.map(r => r.category))];
  let totalPass = 0;
  let totalFail = 0;

  for (const cat of categories) {
    const catResults = results.filter(r => r.category === cat);
    const catPass = catResults.filter(r => r.passed).length;
    const catFail = catResults.filter(r => !r.passed).length;
    totalPass += catPass;
    totalFail += catFail;
    const mark = catFail === 0 ? "✅" : "❌";
    console.log(`  ${mark}  ${cat}  ${catPass}/${catResults.length}`);
  }

  console.log("───────────────────────────────────────────────────────────────");
  const totalTests = totalPass + totalFail;
  console.log(`  Total:  ${totalPass}/${totalTests} PASS  |  ${totalFail} FAIL`);
  console.log(`  allPass: ${totalFail === 0}`);
  console.log("═══════════════════════════════════════════════════════════════");

  if (totalFail > 0) {
    console.log("\nFailed tests:");
    results.filter(r => !r.passed).forEach(r => {
      console.log(`  ❌  [${r.category}] ${r.name}: ${r.detail}`);
    });
    process.exit(1);
  }
}

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