import { applicantProfileSchema, type ApplicantProfile } from "./schema";
import { computeBmi, buildClassFromBmi } from "./bmi";
import { extractHealthPatch } from "./health";
import { extractPersonName, isPlausibleName, parseUsState } from "./usGeo";

export function mergeProfile(base: ApplicantProfile, patch: unknown): ApplicantProfile {
  const parsed = applicantProfileSchema.partial().safeParse(patch || {});
  const p = parsed.success ? parsed.data : {};
  const next: ApplicantProfile = { ...base };
  for (const [k, v] of Object.entries(p)) {
    if (v === undefined || v === null) continue;
    if (k === "name") {
      const incoming = String(v).trim();
      if (!isPlausibleName(incoming)) continue;
      if (next.name && isPlausibleName(next.name) && !isPlausibleName(incoming)) continue;
      if (
        next.name &&
        isPlausibleName(next.name) &&
        incoming.split(/\s+/).length < next.name.split(/\s+/).length
      )
        continue;
      next.name = incoming;
      continue;
    }
    if (k === "state" || k === "property_state") {
      const st = parseUsState(String(v));
      if (!st) continue;
      next.state = st;
      next.property_state = st;
      continue;
    }
    if (k === "impairments" && Array.isArray(v)) {
      const by = new Map((next.impairments || []).map((i) => [i.module, i]));
      for (const item of v as NonNullable<ApplicantProfile["impairments"]>) {
        const prev = by.get(item.module);
        by.set(item.module, {
          module: item.module,
          attributes: { ...(prev?.attributes || {}), ...(item.attributes || {}) },
          path_answers: [...(prev?.path_answers || []), ...(item.path_answers || [])].slice(-12),
        });
      }
      next.impairments = [...by.values()];
      continue;
    }
    if (k === "disease_screens" && v && typeof v === "object") {
      next.disease_screens = { ...(next.disease_screens || {}), ...(v as Record<string, "yes" | "no" | "unsure">) };
      continue;
    }
    (next as Record<string, unknown>)[k] = v;
  }
  const bmi = computeBmi(next.height_ft_in, next.weight_lb);
  if (bmi != null) {
    next.bmi_computed = bmi;
    next.build_class_label = next.build_class_label || buildClassFromBmi(bmi);
  }
  if (next.tobacco_products?.length || next.nicotine_class) next.tobacco_answered = true;
  return applicantProfileSchema.parse(next);
}

const ONES: Record<string, number> = {
  one: 1,
  two: 2,
  three: 3,
  four: 4,
  five: 5,
  six: 6,
  seven: 7,
  eight: 8,
  nine: 9,
  ten: 10,
  eleven: 11,
  twelve: 12,
  thirteen: 13,
  fourteen: 14,
  fifteen: 15,
  sixteen: 16,
  seventeen: 17,
  eighteen: 18,
  nineteen: 19,
};
const TENS: Record<string, number> = {
  twenty: 20,
  thirty: 30,
  forty: 40,
  fifty: 50,
  sixty: 60,
  seventy: 70,
  eighty: 80,
  ninety: 90,
};

function parseEnglishAmount(message: string): number | undefined {
  const t = message.toLowerCase().replace(/-/g, " ");
  if (!/thousand|hundred/.test(t)) return undefined;
  const words = t.replace(/[^a-z\s]/g, " ").split(/\s+/).filter(Boolean);
  let total = 0;
  let current = 0;
  for (const w of words) {
    if (ONES[w] != null) current += ONES[w];
    else if (TENS[w] != null) current += TENS[w];
    else if (w === "hundred") current = (current || 1) * 100;
    else if (w === "thousand") {
      total += (current || 1) * 1000;
      current = 0;
    } else if (w === "million") {
      total += (current || 1) * 1_000_000;
      current = 0;
    }
  }
  const n = total + current;
  return n >= 1000 ? n : undefined;
}

function parseMoneyFigures(message: string): number[] {
  const out: number[] = [];
  const re =
    /\$?\s*([\d,]+(?:\.\d+)?)\s*(k|thousand)?(?:\s*(?:usd|dollars?))?/gi;
  let m: RegExpExecArray | null;
  while ((m = re.exec(message))) {
    let n = Number(m[1].replace(/,/g, ""));
    if (!Number.isFinite(n)) continue;
    if (m[2] || /\bk\b/i.test(m[0])) n *= n < 1000 ? 1000 : 1;
    if (/\bk\b/i.test(m[0]) && n < 1000) n *= 1000;
    if (n >= 1000) out.push(n);
  }
  if (!out.length) {
    const words = parseEnglishAmount(message);
    if (words) out.push(words);
  }
  return out;
}

/** Lightweight extract when OpenAI is unavailable */
export function heuristicPatch(
  message: string,
  ctx?: { lastAssistant?: string; profile?: ApplicantProfile }
): Partial<ApplicantProfile> {
  const patch: Partial<ApplicantProfile> = {};
  const t = message.toLowerCase();
  if (/\bno diseases?\b|\bno conditions\b/.test(t) && /none|no (history|diagnos)/.test(t)) {
    patch.diseases_none_reported = true;
    patch.diseases_complete = true;
  }
  const negativeMoney = /-\s*\$|\$\s*-/.test(message);
  const figures = parseMoneyFigures(message);
  const spouseIncomeCue =
    /spouse.{0,60}(income|make|earn|salary)|(husband|wife).{0,40}(income|make|earn|salary)|spouse income/i.test(t);
  const selfIncomeCue =
    /annual income|yearly income|\bi make\b|\bi earn\b|my income|my salary|salary is|per year|annually|a year|household (income|makes)/i.test(
      t
    ) ||
    (/income|salary|earn/i.test((ctx?.lastAssistant || "").toLowerCase()) &&
      !/spouse|husband|wife/i.test(ctx?.lastAssistant || ""));
  const spouseIncomeAsked = /spouse|husband|wife/i.test(ctx?.lastAssistant || "") && /income|salary|earn/i.test(ctx?.lastAssistant || "");
  const loanCue = /owe|balance|loan|mortgage|home loan/.test(t);

  if (!negativeMoney && (spouseIncomeCue || spouseIncomeAsked) && figures[0]) {
    if (/i make|i earn|my (annual )?income/i.test(t) && /wife|husband|spouse/i.test(t) && figures[1]) {
      patch.annual_income = figures[0];
      patch.spouse_annual_income = figures[1];
    } else {
      patch.spouse_annual_income = figures[0];
    }
  } else if (!negativeMoney && selfIncomeCue && figures[0]) {
    patch.annual_income = figures[0];
  } else if (!negativeMoney && figures[0] && (loanCue || figures[0] >= 10000)) {
    if (loanCue || !selfIncomeCue) patch.loan_balance = figures[0];
  }
  const years = message.match(/(\d+)\s*years?/);
  if (years && /year|term|left/.test(t)) patch.loan_term_years = Number(years[1]);
  const named = extractPersonName(message);
  if (named) patch.name = named;
  const email = message.match(/\b[\w.+-]+@[\w.-]+\.[A-Za-z]{2,}\b/);
  if (email) patch.email = email[0];
  const phone = message.match(/\b(\d{10,11}|\d{3}[-.\s]\d{3}[-.\s]\d{4})\b/);
  if (phone) patch.phone = phone[1];
  if (/\bboth\b/.test(t) && /loan|cover|us/.test(t)) {
    patch.coverage_on_whom = "both";
    patch.co_borrower_flag = true;
    patch.intent = "mortgage_protection";
  }
  if (/\b(just|only)\s+(my\s+)?(husband|wife|spouse)\b/.test(t) && /cover/.test(t)) {
    patch.coverage_on_whom = "spouse";
    patch.co_borrower_flag = true;
    patch.intent = "mortgage_protection";
  }
  if (
    /^(myself|me)\.?$/i.test(message.trim()) ||
    /\bprimary only\b|\bjust me\b|\bonly me\b|\bonly myself\b|\bselft? only\b|\bmyself only\b|\bon me\b|\bjust on me\b|\bfor myself\b|\bme only\b|\bwant it on me\b/.test(
      t
    )
  ) {
    patch.coverage_on_whom = "primary";
    patch.co_borrower_flag = false;
    patch.intent = "mortgage_protection";
    patch.beneficiary_relationship = "self";
  }
  const usState = parseUsState(message);
  if (usState) {
    patch.state = usState;
    patch.property_state = usState;
  }
  const dob = message.match(/\b(\d{1,2}[/-]\d{1,2}[/-]\d{2,4})\b/);
  if (dob) patch.date_of_birth = dob[1];
  const age =
    message.match(/\b(\d{2})\s*-?\s*(years? old|year-old|yo)\b/i) ||
    message.match(/\bi(?:['’]m| am)\s+(\d{2})\b/i);
  if (age) patch.age = Number(age[1] || age[2]);
  if (
    /mortgage protection|mortgage portection|protect the (home )?loan|protect(ing)? my mortgage/.test(t)
  )
    patch.intent = "mortgage_protection";
  const ht = message.match(/(\d)\s*[\'′]\s*(\d{1,2})/);
  if (ht) patch.height_ft_in = `${ht[1]}'${ht[2]}"`;
  const wt = message.match(/(?<!-)\b(\d{2,3}(?:\.\d+)?)\s*(lbs?|pounds?|kgs?|kilos|kilograms)\b/i);
  if (wt) {
    const n = Number(wt[1]);
    patch.weight_lb = /kg/i.test(wt[2]) ? Math.round(n * 2.20462) : n;
  }
  const ftIn = message.match(/(\d)\s*(?:feet|ft)\s*(\d{1,2})\s*(?:inches|in)?/i);
  if (ftIn) patch.height_ft_in = `${ftIn[1]}'${ftIn[2]}"`;
  Object.assign(patch, extractHealthPatch(message, ctx?.lastAssistant, ctx?.profile));
  if (
    /\bnon-?smoker\b|never smoked|not using any tobacco|don'?t use any (tobacco|nicotine|things)|no tobacco|no nicotine/i.test(
      t
    )
  ) {
    patch.tobacco_products = ["None"];
    patch.nicotine_class = "NT";
    patch.tobacco_answered = true;
  } else if (/\b(vapes?|vaping|e-?cigs?|e-?cigarettes?|e-?cigars?|cigars?)\b/i.test(t)) {
    patch.tobacco_products = ["E-cigar"];
    patch.tobacco_answered = true;
  } else if (/\b(cigarettes?|smoker)\b/.test(t)) {
    patch.tobacco_products = ["Smoker"];
    patch.tobacco_answered = true;
  }
  const last = (ctx?.lastAssistant || "").toLowerCase();
  if (
    (/tobacco|nicotine|smok|cigar/.test(last) || /tobacco|nicotine/.test(t)) &&
    /^(no|nope|never|no never)$/i.test(message.trim())
  ) {
    patch.tobacco_products = ["None"];
    patch.nicotine_class = "NT";
    patch.tobacco_answered = true;
  }
  if (/no (diagnos|disease|conditions)|none of those/.test(t) && !/diabet|diabtic/i.test(t)) {
    patch.diseases_none_reported = true;
    patch.diseases_complete = true;
  }
  if (/spouse|husband|wife/.test(t) && /(\d{2})/.test(t)) {
    const a = message.match(/(\d{2})/);
    if (a) patch.spouse_age_sex = a[1];
  }
  if (/beneficiary|leave it to/.test(t)) {
    if (/spouse/.test(t)) patch.beneficiary_relationship = "spouse";
    else patch.beneficiary_relationship = "family";
  }
  return patch;
}
