import assert from "node:assert/strict";
import test from "node:test";
import {
  computeProcess,
  detectStopPath,
  hardStopForProcess,
  isHardStop,
  normalizeStopPath,
  resolveModelStopPath,
} from "@/lib/mortgage/gates";
import { emptyProfile } from "@/lib/mortgage/schema";

/** Mirrors chat/route.ts enqueueCase serialization. */
function makeEnqueue() {
  const caseQueues = new Map<string, Promise<unknown>>();
  function enqueueCase<T>(caseId: string, task: () => Promise<T>): Promise<T> {
    const prev = caseQueues.get(caseId) ?? Promise.resolve();
    const run = prev.then(task, task);
    caseQueues.set(
      caseId,
      run.then(
        () => undefined,
        () => undefined
      )
    );
    return run;
  }
  return enqueueCase;
}

test("RAPID-01 per-case queue serializes concurrent turns", async () => {
  const enqueue = makeEnqueue();
  const order: string[] = [];
  let active = 0;
  let maxActive = 0;

  async function turn(label: string, delayMs: number) {
    active += 1;
    maxActive = Math.max(maxActive, active);
    order.push(`start:${label}`);
    await new Promise((r) => setTimeout(r, delayMs));
    order.push(`end:${label}`);
    active -= 1;
    return label;
  }

  const results = await Promise.all([
    enqueue("case-a", () => turn("A", 40)),
    enqueue("case-a", () => turn("B", 10)),
    enqueue("case-a", () => turn("C", 5)),
  ]);

  assert.deepEqual(results, ["A", "B", "C"]);
  assert.deepEqual(order, ["start:A", "end:A", "start:B", "end:B", "start:C", "end:C"]);
  assert.equal(maxActive, 1);
});

test("RAPID-02 different cases can run in parallel", async () => {
  const enqueue = makeEnqueue();
  let maxActive = 0;
  let active = 0;

  async function turn() {
    active += 1;
    maxActive = Math.max(maxActive, active);
    await new Promise((r) => setTimeout(r, 30));
    active -= 1;
  }

  await Promise.all([enqueue("c1", turn), enqueue("c2", turn)]);
  assert.ok(maxActive >= 2);
});

test("HEALTH-SERIALIZE soft stop never blocks process", () => {
  const free = "Wrong product — wants health insurance";
  assert.equal(normalizeStopPath(free), "WR-HEALTH");
  assert.equal(hardStopForProcess(free), null);
  assert.equal(isHardStop(detectStopPath("I want health insurance.")), false);
  const process = computeProcess(emptyProfile(), hardStopForProcess(free));
  assert.equal(process.blocked, false);
});

test("HEALTH-MODEL-01 model WR-LOAN must not override health soft path", () => {
  const heuristic = detectStopPath("I want health insurance.");
  assert.equal(heuristic, "WR-HEALTH");
  const resolved = resolveModelStopPath({
    heuristicStop: heuristic,
    currentStop: heuristic,
    rawModelStop: "WR-LOAN",
  });
  assert.equal(resolved, "WR-HEALTH");
  assert.equal(isHardStop(resolved), false);
});

test("HEALTH-MODEL-02 model free-form health stays soft", () => {
  const heuristic = detectStopPath("I want health insurance.");
  const resolved = resolveModelStopPath({
    heuristicStop: heuristic,
    currentStop: heuristic,
    rawModelStop: "Wrong product — wants health insurance",
  });
  assert.equal(resolved, "WR-HEALTH");
  assert.equal(hardStopForProcess(resolved), null);
});

test("HEALTH-MODEL-03 genuine refinance still hard-stops when heuristic agrees", () => {
  const msg = "I want to refinance my rate";
  const heuristic = detectStopPath(msg);
  assert.equal(heuristic, "WR-LOAN");
  const resolved = resolveModelStopPath({
    heuristicStop: heuristic,
    currentStop: heuristic,
    rawModelStop: "WR-LOAN",
  });
  assert.equal(resolved, "WR-LOAN");
  assert.equal(isHardStop(resolved), true);
});

test("HEALTH-MODEL-04 model-only WR-LOAN without heuristic is ignored", () => {
  const resolved = resolveModelStopPath({
    heuristicStop: null,
    currentStop: null,
    rawModelStop: "WR-LOAN",
  });
  assert.equal(resolved, null);
});
