/**
 * VF — #50 P4 recommended review action (advisory-only).
 *
 * In-process harness over the PURE recommendAction(score, contributions). Proves
 * the recommendation is honest-by-construction:
 *   Leg 1  priority band both-dir — score >= 75 → HIGH ("review first"), else MEDIUM
 *   Leg 2  feature-mapping both-dir — top feature drives the action; the OTHER
 *          features' actions are NOT produced (an amount-driven detection never
 *          gets the velocity/channel/recipient action, etc.)
 *   Leg 3  advisoryOnly === true on EVERY recommendation (structural — no acting path)
 *   Leg 4  no fabricated identifiers / no enforcement-or-fraud lexicon — the action
 *          never says contain/block/freeze/reverse/kill/pid/fraud/suspicious, and
 *          never echoes the userId/kytResultId as an operational target
 *   Leg 5  deterministic — same (score, contributions) → identical recommendation
 *          (the anti-Math.random property, P1 leg-1 sibling)
 *   Leg 6  thin/empty contributions must NOT invent — drivingFeature "composite",
 *          a generic action that names NO specific feature, still advisory
 *
 * Exit 0 iff all pass.
 */

import { recommendAction, type AnomalyRecommendation } from "../server/lib/transaction-anomaly/recommend";
import type { FeatureContribution, FeatureName } from "../server/lib/transaction-anomaly/types";

const contrib = (feature: FeatureName, contribution: number): FeatureContribution => ({
  feature,
  contribution,
  detail: `${feature} deviation`,
});

// A detection whose top (largest-contribution) feature is `feature`.
const topDrivenBy = (feature: FeatureName): FeatureContribution[] => {
  const others: FeatureName[] = (["amount", "velocity", "channel", "recipientType"] as FeatureName[]).filter(
    (f) => f !== feature,
  );
  return [contrib(feature, 40), ...others.map((f) => contrib(f, 5))];
};

const FEATURES: FeatureName[] = ["amount", "velocity", "channel", "recipientType"];

// A token UNIQUE to each feature's own action (both-directions check). "account
// holder" is deliberately NOT used — it appears in both the amount and channel
// actions (each may legitimately reference the holder); the distinct GUIDANCE is
// what must not bleed, so each probe targets a token that appears in one action only.
const FEATURE_SIGNATURE: Record<FeatureName, RegExp> = {
  amount: /usual range/i,
  velocity: /rapid-drain|account-takeover/i,
  channel: /step-up/i,
  recipientType: /payee/i,
};

const FORBIDDEN = /\b(contain|containment|block|freeze|reverse|quarantine|kill|pid|process|host|fraud|fraudulent|suspicious)\b/i;

type Leg = { name: string; pass: boolean; detail: string };
const legs: Leg[] = [];
const rec = (name: string, pass: boolean, detail: string) => legs.push({ name, pass, detail });

function main() {
  // Leg 1 — priority band both directions.
  {
    const hi = recommendAction(80, topDrivenBy("amount"));
    const at = recommendAction(75, topDrivenBy("amount"));
    const lo = recommendAction(60, topDrivenBy("amount"));
    const ok = hi.priority === "HIGH" && at.priority === "HIGH" && lo.priority === "MEDIUM";
    rec("Leg 1 priority band both-dir", ok, "80→HIGH, 75→HIGH (boundary), 60→MEDIUM");
  }

  // Leg 2 — feature-mapping both directions.
  {
    let ok = true;
    const notes: string[] = [];
    for (const f of FEATURES) {
      const r = recommendAction(70, topDrivenBy(f));
      const mapsRight = r.drivingFeature === f && FEATURE_SIGNATURE[f].test(r.action);
      // both-dir: the action must NOT carry any OTHER feature's signature.
      const noBleed = FEATURES.filter((g) => g !== f).every((g) => !FEATURE_SIGNATURE[g].test(r.action));
      if (!(mapsRight && noBleed)) { ok = false; notes.push(`${f}:map=${mapsRight},noBleed=${noBleed}`); }
    }
    rec("Leg 2 feature-mapping both-dir", ok, ok ? "each top feature → its own action, no cross-bleed" : notes.join(" "));
  }

  // Leg 3 — advisoryOnly on every recommendation.
  {
    const all: AnomalyRecommendation[] = [
      ...FEATURES.map((f) => recommendAction(90, topDrivenBy(f))),
      recommendAction(51, []),
      recommendAction(100, topDrivenBy("recipientType")),
    ];
    const ok = all.every((r) => r.advisoryOnly === true);
    rec("Leg 3 advisoryOnly always true", ok, `${all.length}/${all.length} recommendations advisory-only`);
  }

  // Leg 4 — no fabricated identifiers / no enforcement-or-fraud lexicon.
  {
    const uid = "user-abc-123";
    const kyt = "kyt-xyz-789";
    const samples: string[] = [
      ...FEATURES.map((f) => recommendAction(88, topDrivenBy(f)).action),
      recommendAction(55, []).action,
    ];
    const noForbidden = samples.every((a) => !FORBIDDEN.test(a));
    const noEcho = samples.every((a) => !a.includes(uid) && !a.includes(kyt));
    rec("Leg 4 no fabrication / no enforcement-or-fraud lexicon", noForbidden && noEcho,
      `noForbidden=${noForbidden}, noIdEcho=${noEcho} (contain/block/freeze/kill/pid/fraud/suspicious absent)`);
  }

  // Leg 5 — deterministic.
  {
    const c = topDrivenBy("velocity");
    const a = recommendAction(72, c);
    const b = recommendAction(72, c);
    rec("Leg 5 deterministic", JSON.stringify(a) === JSON.stringify(b), "same (score, contributions) → identical recommendation");
  }

  // Leg 6 — thin/empty contributions must not invent.
  {
    const r = recommendAction(60, []);
    const composite = r.drivingFeature === "composite";
    // Must name NO specific feature — none of the four feature signatures appear.
    const noSpecific = FEATURES.every((f) => !FEATURE_SIGNATURE[f].test(r.action));
    const stillAdvisory = r.advisoryOnly === true && !FORBIDDEN.test(r.action);
    rec("Leg 6 thin contributions → generic, not invented", composite && noSpecific && stillAdvisory,
      `drivingFeature=${r.drivingFeature}, namesNoFeature=${noSpecific}, advisory=${stillAdvisory}`);
  }

  console.log("\n=== VF transaction-anomaly P4 (recommended review action) ===\n");
  let failed = 0;
  for (const l of legs) {
    console.log(`${l.pass ? "PASS" : "FAIL"}  ${l.name} — ${l.detail}`);
    if (!l.pass) failed++;
  }
  console.log(`\n${legs.length - failed}/${legs.length} legs passed.`);
  if (failed > 0) { console.log(`\nGATE: FAIL (${failed})`); process.exit(1); }
  console.log("\nGATE: GREEN — advisory-only recommendation: priority=review-order (not fraud), feature-mapped both-dir, deterministic, no fabrication/enforcement lexicon, thin-input stays generic.");
}

main();
