/**
 * Local runtime probe — exercises gate/extract/serialize logic and writes NDJSON debug evidence.
 * Does not call OpenAI; proves deterministic layers for Issues 1–4.
 */
import fs from "node:fs";
import path from "node:path";
import {
  computeProcess,
  detectStopPath,
  hardStopForProcess,
  isHardStop,
  normalizeStopPath,
} from "../lib/mortgage/gates";
import { heuristicPatch, mergeProfile } from "../lib/mortgage/extract";
import { emptyProfile } from "../lib/mortgage/schema";

const LOG = path.resolve(__dirname, "../../debug-d05587.log");

function log(hypothesisId: string, location: string, message: string, data: Record<string, unknown>) {
  const line = JSON.stringify({
    sessionId: "d05587",
    runId: "local-probe",
    hypothesisId,
    location,
    message,
    data,
    timestamp: Date.now(),
  });
  fs.appendFileSync(LOG, line + "\n");
  console.log(line);
}

function simulateSerialize(stopPath: string | null, profile = emptyProfile()) {
  const hard = hardStopForProcess(stopPath);
  const process = computeProcess(profile, hard);
  let status = hard ? "stopped" : "open";
  return { status, stopPath: hard, blocked: process.blocked, process };
}

async function main() {
  // I1: health intent vs knowledge
  for (const msg of ["I want health insurance.", "Is mortgage protection the same as health insurance?"]) {
    const heuristic = detectStopPath(msg);
    const freeForm = "Wrong product — wants health insurance";
    const afterNorm = normalizeStopPath(freeForm);
    const serHeuristic = simulateSerialize(heuristic);
    const serFree = simulateSerialize(freeForm);
    log("I1", "probe:health", msg, {
      heuristic,
      hardHeuristic: isHardStop(heuristic),
      freeFormNorm: afterNorm,
      hardFree: isHardStop(freeForm),
      statusHeuristic: serHeuristic.status,
      statusFree: serFree.status,
      blockedHeuristic: serHeuristic.blocked,
      blockedFree: serFree.blocked,
    });
  }

  // I4: invalid values
  const negLoan = mergeProfile(emptyProfile(), heuristicPatch("loan balance is -100000"));
  const futDob = mergeProfile(emptyProfile(), heuristicPatch("My DOB is 01/01/2035"));
  const isoDob = mergeProfile(emptyProfile(), heuristicPatch("DOB 2035-01-01"));
  log("I4", "probe:validation", "invalid values rejected", {
    negLoan: negLoan.loan_balance ?? null,
    futDob: futDob.date_of_birth ?? null,
    isoDob: isoDob.date_of_birth ?? null,
  });

  // I2: loan persists across later fields
  let p = emptyProfile();
  p = mergeProfile(p, heuristicPatch("My name is Pat Lee"));
  p = mergeProfile(p, heuristicPatch("Texas"));
  p = mergeProfile(p, heuristicPatch("loan balance 180000 usd, 20 years left"));
  const loanAfter = p.loan_balance;
  p = mergeProfile(p, heuristicPatch(`5'8" 160 lb non-smoker`));
  p = mergeProfile(p, heuristicPatch("none of those"));
  const miss = computeProcess(p).missingRequired;
  log("I2", "probe:loan-persist", "loan after later turns", {
    loanAfter,
    loanFinal: p.loan_balance ?? null,
    reasksLoan: miss.includes("loan_balance"),
    missing: miss.slice(0, 10),
  });

  // I3: queue serialization smoke (inline)
  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;
  }
  const order: string[] = [];
  let maxActive = 0;
  let active = 0;
  await Promise.all(
    ["A", "B", "C"].map((label, i) =>
      enqueueCase("case", async () => {
        active += 1;
        maxActive = Math.max(maxActive, active);
        order.push(`start:${label}`);
        await new Promise((r) => setTimeout(r, 20 - i * 5));
        order.push(`end:${label}`);
        active -= 1;
      })
    )
  );
  log("I3", "probe:queue", "serialized order", { order, maxActive });
}

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