import assert from "node:assert/strict";
import test from "node:test";
import { computeProcess, missingRequired } from "@/lib/mortgage/gates";
import { emptyProfile } from "@/lib/mortgage/schema";
import { heuristicPatch, mergeProfile } from "@/lib/mortgage/extract";
import {
  extractPersonName,
  isGreetingOnly,
  isNonUsLocation,
  isPlausibleName,
  parseUsState,
} from "@/lib/mortgage/usGeo";

test("S0-01 name and Texas in one message", () => {
  const p = mergeProfile(emptyProfile(), heuristicPatch("Hi, I'm John Smith from Texas"));
  assert.equal(p.name, "John Smith");
  assert.equal(p.state, "TX");
  assert.equal(computeProcess(p).step, 1);
});

test("S0-02 phone and Ohio", () => {
  const p = mergeProfile(emptyProfile(), heuristicPatch("You can reach me at 555-123-4567, I'm in Ohio"));
  assert.equal(p.phone, "555-123-4567");
  assert.equal(p.state, "OH");
  assert.equal(computeProcess(p).step, 1);
});

test("S0-03 California full name", () => {
  const p = mergeProfile(emptyProfile(), heuristicPatch("My name is Maria Lopez, I live in California"));
  assert.equal(p.name, "Maria Lopez");
  assert.equal(p.state, "CA");
});

test("S0-04 greeting only is not Hawaii", () => {
  assert.equal(isGreetingOnly("Hi"), true);
  assert.equal(parseUsState("Hi"), null);
  const p = mergeProfile(emptyProfile(), heuristicPatch("Hi"));
  assert.equal(p.name, undefined);
  assert.equal(p.state, undefined);
  assert.equal(computeProcess(p).step, 0);
});

test("S0-05 name without state stays step 0", () => {
  const p = mergeProfile(emptyProfile(), heuristicPatch("This is Alex Johnson"));
  assert.equal(p.name, "Alex Johnson");
  assert.ok(missingRequired(p, 0).includes("state"));
  assert.equal(computeProcess(p).step, 0);
});

test("S0-06 Nevada without name or phone", () => {
  const p = mergeProfile(emptyProfile(), heuristicPatch("I'm in Nevada"));
  assert.equal(p.state, "NV");
  assert.ok(missingRequired(p, 0).includes("name_or_phone"));
  assert.equal(computeProcess(p).step, 0);
});

test("S0-07 standalone ambiguous abbreviations are not states", () => {
  for (const w of ["hi", "in", "or", "me", "ok"]) {
    assert.equal(parseUsState(w), null, w);
  }
});

test("S0-08 Hawaii with cue", () => {
  assert.equal(parseUsState("I live in HI, the Hawaii one"), "HI");
});

test("S0-09 Toronto Canada is non-US and not a state", () => {
  assert.equal(isNonUsLocation("I'm calling from Toronto, Canada"), true);
  const p = mergeProfile(emptyProfile(), heuristicPatch("I'm calling from Toronto, Canada"));
  assert.equal(p.state, undefined);
});

test("S0-10 interview command is not a name", () => {
  assert.equal(extractPersonName("I am only calling about tobacco"), null);
  assert.equal(isPlausibleName("only calling about tobacco"), false);
});

test("S0-11 my self name pattern", () => {
  assert.equal(extractPersonName("My self Rakesh Kumar"), "Rakesh Kumar");
});

test("S0-12 name stops at and", () => {
  assert.equal(extractPersonName("I'm Dave and I live in Texas"), "Dave");
});

test("S0-13 fictional is a name stop word", () => {
  assert.equal(isPlausibleName("fictional person"), false);
  const p = mergeProfile(emptyProfile(), heuristicPatch("My name is fictional person"));
  assert.equal(p.name, undefined);
});

test("AI-081 first matching state wins when two states appear", () => {
  assert.equal(parseUsState("I split time between Texas and Florida"), "TX");
});

test("S1-01 protect my mortgage just on me locks intent and primary", () => {
  const p = heuristicPatch("I want to protect my mortgage, just on me");
  assert.equal(p.intent, "mortgage_protection");
  assert.equal(p.coverage_on_whom, "primary");
});

test("S1-01b mortgage protection + only me does set intent and primary", () => {
  const p = heuristicPatch("I want mortgage protection, only me");
  assert.equal(p.intent, "mortgage_protection");
  assert.equal(p.coverage_on_whom, "primary");
});

test("S1-04 intent without coverage stays step 1 after lead", () => {
  const p = mergeProfile(emptyProfile(), {
    name: "Ann",
    state: "FL",
    ...heuristicPatch("I want mortgage protection"),
  });
  assert.equal(p.intent, "mortgage_protection");
  assert.equal(p.coverage_on_whom, undefined);
  assert.ok(missingRequired(p, 1).includes("coverage_on_whom"));
  assert.equal(computeProcess(p).step, 1);
});

test("S2-01 through S2-04 age DOB and loan", () => {
  assert.equal(heuristicPatch("I'm 42 years old").age, 42);
  assert.equal(heuristicPatch("My birthday is 03/14/1982").date_of_birth, "03/14/1982");
  assert.equal(heuristicPatch("The loan balance is $310,000").loan_balance, 310000);
  assert.equal(heuristicPatch("About 310k on the mortgage").loan_balance, 310000);
});

test("S2-06 bare 250000 with loan cue is loan_balance", () => {
  const p = heuristicPatch("The loan is 250000");
  assert.equal(p.loan_balance, 250000);
});

test("S2-06b dollar loan without age stays step 2", () => {
  const p = mergeProfile(emptyProfile(), {
    name: "Ann",
    state: "FL",
    intent: "mortgage_protection",
    coverage_on_whom: "primary",
    ...heuristicPatch("The loan is $250,000"),
  });
  assert.equal(p.loan_balance, 250000);
  assert.ok(missingRequired(p, 2).includes("date_of_birth"));
  assert.equal(computeProcess(p).step, 2);
});

test("S2-07 age without loan stays step 2", () => {
  const p = mergeProfile(emptyProfile(), {
    name: "Ann",
    state: "FL",
    intent: "mortgage_protection",
    coverage_on_whom: "primary",
    ...heuristicPatch("I'm 55 years old"),
  });
  assert.equal(p.age, 55);
  assert.ok(missingRequired(p, 2).includes("loan_balance"));
  assert.equal(computeProcess(p).step, 2);
});

test("S3-01 lbs and lb both capture weight", () => {
  assert.equal(heuristicPatch(`5'10", 190 lbs`).weight_lb, 190);
  const p = mergeProfile(emptyProfile(), heuristicPatch(`5'10" 190 lb`));
  assert.equal(p.height_ft_in, `5'10"`);
  assert.equal(p.weight_lb, 190);
  assert.equal(p.bmi_computed, 27.3);
});

test("S3-02 kg conversion", () => {
  const p = mergeProfile(emptyProfile(), heuristicPatch("I'm 5'8 and 82 kg"));
  assert.equal(p.weight_lb, 181);
});

test("S3-03 non-smoker", () => {
  const p = heuristicPatch("No tobacco, never smoked");
  assert.equal(p.tobacco_answered, true);
  assert.equal(p.nicotine_class, "NT");
  assert.deepEqual(p.tobacco_products, ["None"]);
});

test("S3-04 I smoke without the word smoker is not captured", () => {
  assert.equal(heuristicPatch("Yes, I smoke about a pack a week").tobacco_products, undefined);
});

test("S3-04b I am a smoker is captured", () => {
  assert.deepEqual(heuristicPatch("Yes, I am a smoker").tobacco_products, ["Smoker"]);
});

test("S3-06 height missing", () => {
  const p = mergeProfile(emptyProfile(), {
    name: "Ann",
    state: "FL",
    intent: "mortgage_protection",
    coverage_on_whom: "primary",
    age: 40,
    loan_balance: 200000,
    ...heuristicPatch("190 lbs, non smoker"),
  });
  assert.ok(missingRequired(p, 3).includes("height_ft_in"));
});

test("S8-01 handoff ack completes missingRequired", () => {
  const p = mergeProfile(emptyProfile(), {
    name: "Ann",
    state: "FL",
    intent: "mortgage_protection",
    coverage_on_whom: "primary",
    age: 40,
    loan_balance: 200000,
    height_ft_in: "5'6\"",
    weight_lb: 140,
    tobacco_answered: true,
    diseases_complete: true,
    annual_income: 85000,
    beneficiary_relationship: "self",
    decision_result: {
      offer_family: "si_level",
      reasons: ["x"],
      drivers: [],
      suggested_next_step: "agent",
    },
    carrier_shortlist: ["Appointed SI carrier A"],
    agent_handoff_ack: true,
  });
  assert.equal(missingRequired(p, 8).length, 0);
  assert.equal(computeProcess(p).step, 8);
});
