import assert from "node:assert/strict";
import test from "node:test";
import { heuristicPatch, mergeProfile } from "@/lib/mortgage/extract";
import { applicantProfileSchema, emptyProfile, modelChatResponseSchema } from "@/lib/mortgage/schema";
import { extractPersonName } from "@/lib/mortgage/usGeo";

/** Schema fields that exist but heuristic extract.ts never sets. Dataset S/O — LLM-only in current app. */

test("heuristic does not capture marital_status from I am married", () => {
  const p = heuristicPatch("I am married");
  assert.equal(p.marital_status, undefined);
});

test("I make 4000 a month is stored as annual_income 4000 (no monthly×12)", () => {
  const p = heuristicPatch("I make 4000 a month");
  assert.equal(p.annual_income, 4000);
});

test("heuristic does not capture net_worth existing_coverage bankruptcy dependents spouse_tobacco citizenship labs family_history", () => {
  const p = heuristicPatch(
    "net worth 2 million, existing coverage 100000, bankruptcy 3 years ago, two kids, spouse smokes, US citizen, A1C 6.2, father had heart disease"
  );
  assert.equal(p.net_worth, undefined);
  assert.equal(p.existing_coverage_amount, undefined);
  assert.equal(p.bankruptcy_years, undefined);
  assert.equal(p.dependent_children_count, undefined);
  assert.equal(p.spouse_tobacco, undefined);
  assert.equal(p.citizenship_residency, undefined);
  assert.equal(p.family_history, undefined);
  assert.equal(p.labs, undefined);
});

test("schema still accepts those LLM-only fields when patched explicitly", () => {
  const parsed = applicantProfileSchema.parse({
    marital_status: "married",
    net_worth: 2000000,
    existing_coverage_amount: 100000,
    bankruptcy_years: 3,
    dependent_children_count: 2,
    spouse_tobacco: "yes",
    citizenship_residency: "US",
    family_history: { fh_cvd_parent_sibling: "father" },
    labs: { a1c_bp_etc: "6.2" },
  });
  assert.equal(parsed.marital_status, "married");
  assert.equal(parsed.net_worth, 2000000);
});

test("short phone is not captured (no validation error object in current extractor)", () => {
  const p = heuristicPatch("my phone is 123");
  assert.equal(p.phone, undefined);
});

test("10-digit phone is captured", () => {
  assert.equal(heuristicPatch("call me at 5551234567").phone, "5551234567");
});

test("XSS payload is stored as text, not executed, and is not a plausible name", () => {
  const p = mergeProfile(emptyProfile(), heuristicPatch("<script>alert(1)</script> My name is Pat Lee"));
  assert.equal(p.name, "Pat Lee");
  const raw = heuristicPatch("<img src=x onerror=alert(1)>");
  assert.equal(raw.name, undefined);
  const merged = mergeProfile(emptyProfile(), {
    impairments: [{ module: "note", attributes: { text: "<script>x</script>" } }],
  });
  assert.equal(merged.impairments?.[0].attributes.text, "<script>x</script>");
});

test("SQL-like chat text does not become loan_balance", () => {
  const p = heuristicPatch("'; DROP TABLE customers; --");
  assert.equal(p.loan_balance, undefined);
});

test("modelChatResponseSchema rejects missing reply and bad patch types", () => {
  assert.equal(modelChatResponseSchema.safeParse({}).success, false);
  assert.equal(modelChatResponseSchema.safeParse({ reply_to_customer: "Hi" }).success, true);
  assert.equal(
    modelChatResponseSchema.safeParse({ reply_to_customer: "Hi", profile_patch: { age: "forty" } }).success,
    false
  );
});

test("malformed model JSON parses to null using the same contract as openai.ts", () => {
  function parseModelRaw(raw: string) {
    try {
      const parsed = modelChatResponseSchema.safeParse(JSON.parse(raw));
      if (!parsed.success) return null;
      return parsed.data;
    } catch {
      return null;
    }
  }
  assert.equal(parseModelRaw("not-json"), null);
  assert.equal(parseModelRaw("{}"), null);
  const ok = parseModelRaw(JSON.stringify({ reply_to_customer: "Thanks — which state?" }));
  assert.equal(ok?.reply_to_customer, "Thanks — which state?");
});

test("accented given name is truncated to Ren before the non-ASCII letter", () => {
  assert.equal(extractPersonName("My name is Renée O'Connor"), "Ren");
});
