// Patient state hook + sample seed data + helpers.
const { useState, useEffect, useMemo, useRef, useCallback } = React;

const DIAGNOSES = [
  { id: "ckd",       label: "CKD (Stages 3–5 non-dialysis)", short: "CKD",   tier: "deep",     hint: "KDOQI 2020" },
  { id: "obesity",   label: "Obesity / Weight management",    short: "Obesity", tier: "deep",   hint: "AND EBNPG" },
  { id: "t2dm",      label: "Type 2 Diabetes",                 short: "T2DM",  tier: "standard", hint: "AND Diabetes EBNPG" },
  { id: "prediabetes", label: "Prediabetes",                   short: "Prediabetes", tier: "standard", hint: "AND Diabetes EBNPG" },
  { id: "wound",     label: "Wound healing / Pressure injury", short: "Wound", tier: "standard", hint: "NPIAP" },
  { id: "elderly",   label: "Sarcopenia / Elderly malnutrition risk", short: "Sarcopenia", tier: "standard", hint: "ESPEN Geriatrics" },
  { id: "healthy",   label: "Healthy adult / Maintenance",     short: "Healthy", tier: "standard", hint: "AND Adult Nutrition" },
  { id: "other",     label: "Other (free text)",                short: "Other", tier: "fallback", hint: "general principles" },
];

// Short diagnosis label for chips / case labels. CKD appends stage when known;
// "Other" derives from the free text (HD, Cancer…) and falls back to the text itself.
function dxShort(state) {
  const d = state?.diagnosis;
  if (!d) return null;
  if (d.id === "ckd") return state.ckdStage ? `CKD ${state.ckdStage}` : d.short;
  if (d.id === "other") {
    const t = (state.diagnosisOther || "").trim();
    if (!t) return d.short;
    if (/hemodialysis|\bhd\b|dialysis|eskd|esrd/i.test(t)) return "HD";
    if (/cancer|carcinoma|oncolog|chemo|malignan/i.test(t)) return "Cancer";
    return t;
  }
  return d.short;
}

const TIER_LABEL = {
  deep:     "Deep logic",
  standard: "Standard defaults",
  fallback: "General fallback",
};
const TIER_HINT = {
  deep:     "Diagnosis-specific calculation logic, citations, and copilot rules.",
  standard: "Standard formula defaults with diagnosis-aware adjustments.",
  fallback: "General nutrition principles — no diagnosis-specific logic.",
};

// v1d S2: Prediabetes promoted to a primary diagnosis; removed here so it can't be double-entered.
const COMORBIDITIES = [
  "Hypertension", "Hyperlipidemia", "Overweight",
  "Underweight/Malnutrition", "Dialysis", "Cancer", "GI Disorder",
];

const ACTIVITY = [
  { id: "sedentary",  label: "Sedentary",          mult: 1.2,   hint: "little/no exercise" },
  { id: "light",      label: "Lightly Active",      mult: 1.375, hint: "1–3 days/wk" },
  { id: "moderate",   label: "Moderately Active",   mult: 1.55,  hint: "3–5 days/wk" },
  { id: "very",       label: "Very Active",         mult: 1.725, hint: "6–7 days/wk" },
  { id: "extra",      label: "Extra Active",        mult: 1.9,   hint: "physical job + training" },
];

const FOOD_SECURITY = [
  { id: "secure",   label: "Food Secure",   cls: "secure" },
  { id: "limited",  label: "Limited Access", cls: "limited" },
  { id: "insecure", label: "Food Insecure",  cls: "insecure" },
];

const EMPTY_STATE = {
  age: "",
  sex: "",                 // "F" | "M" | "Other"
  height: "",              // string (input value)
  heightUnit: "cm",        // "cm" | "in"
  weight: "",
  weightUnit: "kg",        // "kg" | "lb"
  weightHistory: "",
  diagnosis: null,         // diagnosis object or null
  diagnosisOther: "",
  comorbidities: [],
  activity: "",
  dietNotes: "",
  foodSecurity: "",
  feedingAssistance: false,
  advanceDirectives: false,
  interpreter: false,
  interpreterLang: "",
  labs: "",
};

const dx = id => DIAGNOSES.find(d => d.id === id);
const sample = (o) => ({ ...EMPTY_STATE, ...o });
const SAMPLE_PATIENTS = [
  sample({ age: "52", sex: "F", height: "163", weight: "89.5", diagnosis: dx("t2dm"),
    comorbidities: ["Hypertension", "Hyperlipidemia"], activity: "light", foodSecurity: "limited",
    labs: "HbA1c 8.2%, LDL 142", dietNotes: "Skips breakfast, large dinner, 2 sodas/day." }),
  sample({ age: "74", sex: "M", height: "175", weight: "68", diagnosis: dx("ckd"),
    weightHistory: "UBW 76 kg; lost 8 kg over 5 months, unintentional.", activity: "sedentary",
    labs: "eGFR 24, albumin 3.4, K 5.1", dietNotes: "Poor appetite, eats one full meal a day." }),
  sample({ age: "68", sex: "F", height: "160", weight: "54", diagnosis: dx("wound"),
    comorbidities: ["Underweight/Malnutrition"], activity: "sedentary", feedingAssistance: true,
    labs: "Albumin 2.9, prealbumin 14", dietNotes: "Stage 3 sacral pressure injury. Limited mobility. Family reports she eats about half of meals." }),
  sample({ age: "31", sex: "M", height: "180", weight: "82", diagnosis: dx("healthy"),
    activity: "very", foodSecurity: "secure",
    dietNotes: "Strength and running 6 days/week. Wants a protein target for training." }),
  sample({ age: "58", sex: "F", height: "165", weight: "78", diagnosis: dx("prediabetes"),
    comorbidities: ["Hypertension"], activity: "light", foodSecurity: "limited",
    labs: "HbA1c 6.1%, fasting glucose 108", dietNotes: "Relies on canned and frozen food; cooks for grandchildren." }),
  sample({ age: "45", sex: "M", height: "178", weight: "118", diagnosis: dx("obesity"),
    comorbidities: ["Hyperlipidemia", "GI Disorder"], activity: "sedentary", foodSecurity: "secure",
    labs: "LDL 160, TG 210", dietNotes: "GERD. Desk job. Late-night eating; fast food 4–5 times a week." }),
  sample({ age: "63", sex: "F", height: "158", weight: "61", diagnosis: dx("other"), diagnosisOther: "ESKD on hemodialysis 3×/week",
    comorbidities: ["Dialysis", "Hypertension"], activity: "sedentary", advanceDirectives: true,
    labs: "Albumin 3.6, K 5.4, phos 6.2", dietNotes: "Fluid restriction difficult; thirsty in afternoons." }),
  sample({ age: "70", sex: "M", height: "172", weight: "64", diagnosis: dx("other"), diagnosisOther: "Colorectal cancer, on chemotherapy",
    comorbidities: ["Cancer"], weightHistory: "UBW 72 kg; lost 8 kg over 3 months, unintentional.", activity: "sedentary",
    interpreter: true, interpreterLang: "Spanish",
    labs: "Albumin 3.2", dietNotes: "Nausea days 2–4 post-infusion; taste changes." }),
];
let _lastSampleIdx = -1;
function pickSamplePatient() {
  let i;
  do { i = Math.floor(Math.random() * SAMPLE_PATIENTS.length); } while (SAMPLE_PATIENTS.length > 1 && i === _lastSampleIdx);
  _lastSampleIdx = i;
  return SAMPLE_PATIENTS[i];
}
const SAMPLE_PATIENT = SAMPLE_PATIENTS[0]; // kept for backward compatibility

// ---------- Conversions ----------
const toCm = (h, unit) => unit === "in" ? h * 2.54 : h;
const toKg = (w, unit) => unit === "lb" ? w / 2.2046226218 : w;

// BMI thresholds — adjusted for age >=65 per spec.
// v1d S3: classify on the SAME one-decimal value the RD sees, so a displayed 25.0 never reads WNL.
function bmiStatus(rawBmi, age) {
  if (!Number.isFinite(rawBmi)) return null;
  const bmi = Math.round(rawBmi * 10) / 10;
  const elderly = Number.isFinite(age) && age >= 65;
  if (elderly) {
    if (bmi < 23) return { key: "under", label: "Below range", cls: "under" };
    if (bmi <= 30) return { key: "normal", label: "WNL (≥65)", cls: "normal" };
    return { key: "over", label: "Above range", cls: "over" };
  }
  if (bmi < 18.5) return { key: "under", label: "Underweight", cls: "under" };
  if (bmi < 25)   return { key: "normal", label: "WNL", cls: "normal" };
  if (bmi < 30)   return { key: "over", label: "Overweight", cls: "over" };
  if (bmi < 35)   return { key: "obese", label: "Obesity · Class I", cls: "obese" };
  if (bmi < 40)   return { key: "obese", label: "Obesity · Class II", cls: "obese" };
  return { key: "obese", label: "Obesity · Class III", cls: "obese" };
}

function computeBMI(state) {
  const h = parseFloat(state.height);
  const w = parseFloat(state.weight);
  if (!Number.isFinite(h) || !Number.isFinite(w) || h <= 0 || w <= 0) return null;
  const cm = toCm(h, state.heightUnit);
  const kg = toKg(w, state.weightUnit);
  const m = cm / 100;
  return kg / (m * m);
}

// ---------- Citations (curated library) ----------
const CITATIONS = {
  mifflin:  { tag: "Mifflin 1990", full: "Mifflin MD, et al. A new predictive equation for resting energy expenditure in healthy individuals. Am J Clin Nutr. 1990;51(2):241–7.", note: "Endorsed by the Academy of Nutrition and Dietetics for adult populations." },
  harris:   { tag: "Harris-Benedict 1919", full: "Harris JA, Benedict FG. A Biometric Study of Basal Metabolism in Man. Carnegie Institution of Washington; 1919.", note: "Older equation; tends to slightly overestimate REE in modern populations." },
  aspen:    { tag: "ASPEN", full: "ASPEN Clinical Guidelines — adult nutrition support practice recommendations.", note: "Source for activity and stress factor ranges." },
  kdoqi:    { tag: "KDOQI 2020", full: "KDOQI Clinical Practice Guideline for Nutrition in CKD: 2020 Update. Am J Kidney Dis. 2020;76(3 Suppl 1):S1–S107.", note: "Protein 0.55–0.60 g/kg/day for non-dialysis CKD stages 3–5 in metabolically stable adults." },
  obesity:  { tag: "AND Adult Weight Mgmt EBNPG", full: "Academy of Nutrition and Dietetics Adult Weight Management Evidence-Based Nutrition Practice Guideline.", note: "Adjusted body weight is preferred for protein dosing in obesity." },
  npiap:    { tag: "NPIAP", full: "National Pressure Injury Advisory Panel Clinical Practice Guideline; AND wound healing recommendations.", note: "Protein 1.2–1.5 g/kg/day for stage 2+ pressure injury and other significant wounds." },
  espen:    { tag: "ESPEN Geriatrics", full: "ESPEN Guideline on Clinical Nutrition and Hydration in Geriatrics. Clin Nutr. 2019;38(1):10–47.", note: "Protein floor of ≥1.0 g/kg/day for adults ≥65; higher with acute/chronic illness." },
  healthyAdult: { tag: "DRI / AND Adult", full: "Institute of Medicine. Dietary Reference Intakes for Energy, Carbohydrate, Fiber, Fat, Fatty Acids, Cholesterol, Protein, and Amino Acids. 2005; Academy of Nutrition and Dietetics Adult Nutrition Care Manual.", note: "Protein RDA 0.8 g/kg/day for healthy adults; 0.8–1.0 g/kg common practice range." },
  activeAdult: { tag: "Active adult · PLACEHOLDER", full: "Placeholder: 1.2–1.4 g/kg/day for very/extra active healthy or prediabetic adults. Not yet verified against a primary source — flag for RD review before clinical testing.", note: "PLACEHOLDER VALUE. Replace with a verified citation (e.g., ACSM/AND/DC joint position on nutrition and athletic performance) once confirmed." },
  oldAdult: { tag: "AND Older Adults", full: "Academy of Nutrition and Dietetics Position Paper: The Role of Nutrition in Maintaining Health in the Nation's Older Adults.", note: "Supports raised protein floor and adjusted BMI ranges in older adults." },
  malnut:   { tag: "ASPEN/Academy Malnutrition", full: "ASPEN/Academy Consensus Statement: Characteristics Recommended for the Identification and Documentation of Adult Malnutrition. JPEN. 2012.", note: "Source for malnutrition criteria and weight-loss thresholds." },
  diabetes: { tag: "AND Diabetes EBNPG", full: "Academy of Nutrition and Dietetics Type 1 and 2 Diabetes Evidence-Based Nutrition Practice Guideline.", note: "Higher protein within renal-safe range supports satiety and glycemic management." },
  idnt:     { tag: "AND IDNT", full: "Academy of Nutrition and Dietetics International Dietetics & Nutrition Terminology Reference Manual.", note: "Source for nutrition diagnosis (PES) terminology." },
};

// ---------- Energy ----------
function bmrMifflin(kg, cm, age, sex) {
  const constant = sex === "M" ? 5 : -161;  // "Other" treated as female-formula per common clinical default
  return 10 * kg + 6.25 * cm - 5 * age + constant;
}
function bmrHarrisBenedict(kg, cm, age, sex) {
  // Revised 1984 Roza/Shizgal coefficients
  if (sex === "M") return 88.362 + 13.397 * kg + 4.799 * cm - 5.677 * age;
  return 447.593 + 9.247 * kg + 3.098 * cm - 4.330 * age;
}
function mifflinFormulaText(sex) {
  return sex === "M"
    ? "(10 × W) + (6.25 × H) − (5 × A) + 5"
    : "(10 × W) + (6.25 × H) − (5 × A) − 161";
}
function harrisFormulaText(sex) {
  return sex === "M"
    ? "88.362 + (13.397 × W) + (4.799 × H) − (5.677 × A)"
    : "447.593 + (9.247 × W) + (3.098 × H) − (4.330 × A)";
}

const ENERGY_METHODS = [
  { id: "mifflin", label: "Mifflin-St Jeor", recommended: true,  citation: "mifflin", formula: mifflinFormulaText, fn: bmrMifflin },
  { id: "harris",  label: "Harris-Benedict", recommended: false, citation: "harris",  formula: harrisFormulaText,  fn: bmrHarrisBenedict },
];

const CALORIC_ADJUSTMENTS = [
  { id: "minus500", value: -500, label: "−500 kcal · moderate deficit (~0.45 kg/wk loss)" },
  { id: "minus250", value: -250, label: "−250 kcal · gentle deficit (~0.23 kg/wk loss)" },
  { id: "zero",     value:    0, label: "0 kcal · maintenance" },
  { id: "plus250",  value:  250, label: "+250 kcal · gentle surplus (~0.23 kg/wk gain)" },
  { id: "plus500",  value:  500, label: "+500 kcal · moderate surplus (~0.45 kg/wk gain)" },
];

// ---------- Protein ----------
// Returns { low, high, point, basis: "actual"|"adjusted", citation, rationale, geriAdjusted, activityShift }
// `point` is where the default lands in the range: sedentary/light → low, moderate → midpoint, very/extra → high.
// For healthy & prediabetes, very/extra active lifts the range to 1.2–1.4 g/kg (placeholder — see CITATIONS.activeAdult).
function proteinDefaultsFor(diagnosisId, age, activityId) {
  const elderly = Number.isFinite(age) && age >= 65;
  let row;
  switch (diagnosisId) {
    case "ckd":      row = { low: 0.55, high: 0.60, basis: "actual",   citation: "kdoqi",   rationale: "KDOQI-aligned protein restriction for non-dialysis CKD in metabolically stable adults." }; break;
    case "obesity":  row = { low: 1.20, high: 1.50, basis: "adjusted", citation: "obesity", rationale: "Adjusted body weight preferred to avoid over-dosing protein in obesity." }; break;
    case "wound":    row = { low: 1.20, high: 1.50, basis: "actual",   citation: "npiap",   rationale: "Elevated protein supports tissue repair and pressure injury healing." }; break;
    case "elderly":  row = { low: 1.00, high: 1.20, basis: "actual",   citation: "espen",   rationale: "Floor raised to preserve lean mass; consider 1.2–1.5 g/kg if at-risk indicators." }; break;
    case "t2dm":     row = { low: 1.00, high: 1.50, basis: "actual",   citation: "diabetes",rationale: "Higher protein supports satiety and glycemic management within renal-safe limits." }; break;
    case "prediabetes": row = { low: 1.00, high: 1.20, basis: "actual", citation: "diabetes",rationale: "Modestly higher protein supports insulin sensitivity and satiety while glycemia is still reversible." }; break;
    case "healthy":  row = { low: 0.80, high: 1.00, basis: "actual",   citation: "healthyAdult",rationale: "Standard adult maintenance range." }; break;
    default:         row = { low: 0.80, high: 1.00, basis: "actual",   citation: "healthyAdult",rationale: "General adult range; no diagnosis-specific guideline applied." };
  }
  // Geriatric floor: ≥1.0 g/kg minimum, except CKD which has its own evidence base.
  // v1d S1: the older-adult source is cited only when the floor actually changed the range.
  let geriAdjusted = false;
  if (elderly && diagnosisId !== "ckd" && row.low < 1.0) {
    row = { ...row, low: 1.0, high: Math.max(row.high, 1.0), citation: "oldAdult" };
    geriAdjusted = true;
  }
  // Activity shift — where the default lands within the range.
  const act = ACTIVITY.find(a => a.id === activityId);
  let activityShift = null;
  let t = 0.5;
  if (act) {
    if (act.id === "sedentary" || act.id === "light") { t = 0; activityShift = "low"; }
    else if (act.id === "moderate") { t = 0.5; activityShift = "mid"; }
    else { t = 1; activityShift = "high"; }
    if (t === 1 && (diagnosisId === "healthy" || diagnosisId === "prediabetes" || diagnosisId === "other")) {
      row = { ...row, low: 1.2, high: 1.4, citation: "activeAdult" };
    }
    row = { ...row, rationale: `${row.rationale} ${act.label} → default set at the ${activityShift === "mid" ? "midpoint" : activityShift + " end"} of the range.` };
  }
  const point = row.low + (row.high - row.low) * t;
  return { ...row, point, geriAdjusted, activityShift };
}

// ---------- v1d S5: Modifier scaffold (not user-facing; ships with none active) ----------
// Each modifier: { id, label, revealsFields: [], adjustWeight(kg, state), adjustProtein(range, state),
//                  adjustEnergy(kcal, state), noteLine(state), citation }
// Pipeline order: diagnosis defaults → active modifiers (in array order) → RD overrides.
const MODIFIERS = [];
function activeModifiers(state) {
  const ids = state.modifiers || [];
  return MODIFIERS.filter(m => ids.includes(m.id));
}
function applyModifiers(kind, value, state) {
  const fn = { weight: "adjustWeight", protein: "adjustProtein", energy: "adjustEnergy" }[kind];
  return activeModifiers(state).reduce((v, m) => (typeof m[fn] === "function" ? m[fn](v, state) : v), value);
}
function modifierNoteLines(state) {
  return activeModifiers(state).map(m => (typeof m.noteLine === "function" ? m.noteLine(state) : null)).filter(Boolean);
}

// ---------- v1d S4: shared protein weight-basis resolver ----------
// basis: "current" | "ideal" | "adjusted". Returns the kg used for protein plus IBW/ABW for display.
const WEIGHT_BASES = [
  { id: "current",  label: "Current" },
  { id: "ideal",    label: "Ideal (Devine)" },
  { id: "adjusted", label: "Adjusted" },
];
function defaultBasisFor(dxDefaults) {
  return dxDefaults?.basis === "adjusted" ? "adjusted" : "current";
}
function resolveProteinWeight({ weightKg, heightCm, sex }, calc, dxDefaults, state) {
  const canDerive = Number.isFinite(heightCm) && Number.isFinite(weightKg) && (sex === "M" || sex === "F");
  const ibw = canDerive ? devineIBW(heightCm, sex) : null;
  const abw = canDerive ? adjustedBodyWeight(weightKg, ibw) : null;
  const basis = calc.proteinWeightBasis || defaultBasisFor(dxDefaults);
  let kg = weightKg;
  if (basis === "ideal" && Number.isFinite(ibw)) kg = ibw;
  else if (basis === "adjusted" && Number.isFinite(abw)) kg = abw;
  kg = applyModifiers("weight", kg, state || {});
  return { basis, basisLabel: basis, proteinWeight: kg, ibw, abw, canDerive };
}

// IBW (Devine) for adjusted body weight in obesity
function devineIBW(cm, sex) {
  const inches = cm / 2.54;
  const over5ft = Math.max(0, inches - 60);
  const base = sex === "M" ? 50 : 45.5;
  return base + 2.3 * over5ft;
}
function adjustedBodyWeight(actualKg, ibwKg) {
  return ibwKg + 0.25 * (actualKg - ibwKg);
}

// ---------- Fluids ----------
const FLUID_METHODS = [
  { id: "perKcal", label: "1 mL · kcal⁻¹ of energy needs", citation: "aspen" },
  { id: "perKg",   label: "30–35 mL/kg actual body weight",  citation: "aspen" },
];

// ---------- Calc state defaults (override / edit tracking) ----------
const EMPTY_CALC = {
  energyMethod: "mifflin",
  stressFactor: 1.0,
  caloricAdjustment: "zero",
  proteinWeightBasis: null,        // v1d S4: null = follow dx default; else "current"|"ideal"|"adjusted"
  fluidMethod: "perKcal",
  // overrides — null = use computed value
  bmrOverride: null,
  activityMultOverride: null,
  tdeeOverride: null,
  proteinBasisOverride: null,    // numeric g/kg
  fluidValueOverride: null,      // numeric mL
  adjustmentNotes: "",
};

// ---------- PES Builder library ----------
const PROBLEMS = [
  { id: "inadequateEnergy",   label: "Inadequate energy intake" },
  { id: "excessiveEnergy",    label: "Excessive energy intake" },
  { id: "inadequateProtein",  label: "Inadequate protein intake" },
  { id: "excessiveProtein",   label: "Excessive protein intake" },
  { id: "inconsistentCarb",   label: "Inconsistent carbohydrate intake" },
  { id: "excessiveCarb",      label: "Excessive carbohydrate intake" },
  { id: "inadequateFluid",    label: "Inadequate fluid intake" },
  { id: "excessiveFluid",     label: "Excessive fluid intake" },
  { id: "underweight",        label: "Underweight" },
  { id: "overweight",         label: "Overweight / Obesity" },
  { id: "weightLoss",         label: "Unintentional weight loss" },
  { id: "knowledgeDeficit",   label: "Food/Nutrition-Related Knowledge Deficit" },
  { id: "limitedAccess",      label: "Limited access to food" },
];

// Suggested default Problem given diagnosis (used to pre-fill PES — RD can change)
const DX_PROBLEM_DEFAULT = {
  t2dm:    "inconsistentCarb",
  prediabetes: "excessiveCarb",
  obesity: "excessiveEnergy",
  ckd:     "excessiveProtein",
  wound:   "inadequateProtein",
  elderly: "inadequateEnergy",
  healthy: "knowledgeDeficit",
};

// Diagnosis-driven interventions
const DX_INTERVENTIONS = {
  t2dm: [
    "Apply carbohydrate consistency: 45–60 g carbohydrate per meal, distributed across 3 meals + 1–2 snacks to support glycemic stability.",
    "Use the plate method (½ non-starchy vegetables, ¼ lean protein, ¼ whole grains/starch) as a portion-control strategy.",
    "Prioritize low-glycemic-index carbohydrate sources (whole grains, legumes, non-starchy vegetables) and pair carbs with protein/fiber to blunt postprandial glucose.",
    "Limit added sugars and sugar-sweetened beverages; substitute water, unsweetened tea/coffee, or sparkling water.",
  ],
  prediabetes: [
    "Target 5–7% body weight reduction if BMI ≥25 — the strongest evidence for delaying progression to T2DM.",
    "Shift carbohydrate quality toward whole grains, legumes, and non-starchy vegetables; reduce refined starches and sugar-sweetened beverages.",
    "Distribute carbohydrate evenly across meals (~45–60 g/meal) and pair with protein and fiber to blunt postprandial glucose.",
    "Build toward 150 min/week of moderate activity plus 2 resistance sessions to improve insulin sensitivity.",
  ],
  ckd: [
    "Restrict dietary protein to 0.55–0.60 g/kg/day to slow CKD progression in metabolically stable non-dialysis adults (KDOQI 2020).",
    "Limit sodium to <2,300 mg/day; emphasize fresh foods and herb-based seasoning over packaged/processed items.",
    "Monitor potassium and phosphorus per labs — coach on portion sizes for higher-K+ foods (potatoes, tomatoes, oranges, bananas) if labs trend up.",
    "Ensure adequate energy intake (≥30 kcal/kg) to prevent protein catabolism while maintaining the protein restriction.",
  ],
  obesity: [
    "Implement a moderate energy deficit (~500 kcal/day) targeting ~0.45 kg/week weight loss with a behavior-change focus rather than restriction.",
    "Use the plate method and pre-portioning strategies to rebuild satiety cues.",
    "Front-load protein at breakfast and lunch (25–30 g/meal) to support satiety and lean-mass preservation during the deficit.",
    "Establish meal timing rhythm: 3 meals + planned snack window; minimize unstructured grazing.",
  ],
  wound: [
    "Increase protein intake to 1.2–1.5 g/kg/day to support tissue repair and granulation.",
    "Ensure adequate hydration (1 mL/kcal or 30–35 mL/kg) to maintain skin turgor and tissue perfusion.",
    "Emphasize wound-relevant micronutrients: vitamin C, zinc, and vitamin A through whole-food sources.",
    "Schedule protein-distributed meals/snacks (every 3–4 h) rather than bolus loading at one meal.",
  ],
  elderly: [
    "Distribute protein evenly across meals (≥25–30 g/meal) to maximize muscle protein synthesis.",
    "Encourage energy-dense whole foods (nuts, eggs, dairy, olive oil) to meet needs without high meal volume.",
    "Pair adequate protein with weight-bearing/resistance activity as tolerated to preserve lean mass.",
    "Screen for and address common barriers: chewing/swallowing difficulty, taste changes, social isolation around meals.",
  ],
  healthy: [
    "Maintain a vegetable-forward plate; aim for 2½ cups vegetables and 2 cups fruit per day.",
    "Choose whole grains and legumes as primary carbohydrate sources for fiber and micronutrients.",
    "Hydrate to 30–35 mL/kg unless otherwise restricted.",
  ],
  other: [
    "Build balanced meals using the plate method and consistent meal timing.",
    "Emphasize whole foods, fiber, and adequate hydration as a baseline pattern.",
  ],
};

// Comorbidity overlays — added on top of dx interventions
const COMORBIDITY_INTERVENTIONS = {
  Hypertension:  ["Limit sodium to <2,300 mg/day (DASH-aligned); coach label-reading for sodium."],
  Hyperlipidemia:["Limit saturated fat to <7% of energy; replace with monounsaturated fats (olive oil, avocado, nuts)."],
  Dialysis:      ["Coordinate fluid and potassium/phosphorus targets with the dialysis care team; defer to renal dietitian if available."],
  "GI Disorder": ["Tailor fiber type/quantity and meal volume based on tolerance; document trigger foods."],
  Cancer:        ["Prioritize energy and protein density during treatment; manage symptom-driven intake (nausea, taste changes)."],
};

// Food-security overlays
const FOOD_SECURITY_INTERVENTIONS = {
  limited: [
    "Prioritize affordable, widely available substitutions (canned/frozen produce, dried beans, eggs) when fresh items are out of reach.",
    "Provide culturally appropriate, budget-friendly meal ideas; share local food-pantry and SNAP-Ed resources.",
  ],
  insecure: [
    "Connect with case management for SNAP, WIC, and local food-pantry resources before finalizing the dietary plan.",
    "Build the plan around shelf-stable staples (canned beans, peanut butter, oats, rice, frozen vegetables) and skill-light preparation.",
    "Prioritize substitutions over restriction — meet the patient where their access allows.",
  ],
};

// Diagnosis-specific lab/clinical monitoring targets
const DX_MONITORING = {
  t2dm:    ["HbA1c < 7.0% (individualize per geriatric or comorbid considerations)", "Fasting glucose 80–130 mg/dL"],
  prediabetes: ["HbA1c trend toward <5.7% (recheck at 3 months)", "Fasting glucose <100 mg/dL", "Body weight: 5–7% reduction if BMI ≥25"],
  ckd:     ["eGFR trajectory (slope) at next nephrology visit", "Serum potassium, phosphorus, and albumin", "24-h or spot urine protein-to-creatinine ratio"],
  obesity: ["Body weight and waist circumference", "Lipid panel (LDL, HDL, TG) at 3 months"],
  wound:   ["Wound surface area / stage progression at weekly skin check", "Pre-albumin or albumin trend"],
  elderly: ["Body weight, MUAC, and grip strength if available", "Pre-albumin or albumin trend"],
  healthy: ["Body weight stability ± 2.5%", "Annual lipid panel and metabolic screening"],
  other:   ["Body weight stability", "Repeat dietary recall at follow-up"],
};

// PES helper text
const PES_HELP = {
  problem: "Choose the IDNT-aligned nutrition diagnosis that best matches the patient's primary unmet nutrition need.",
  etiology: "Root cause or contributing factor — what the problem is 'related to'. Be specific (knowledge deficit, food access, disease state, medication side effect).",
  signs:    "Measurable evidence — what the problem is 'as evidenced by'. Cite labs, anthropometrics, dietary recall data, or documented behaviors.",
};

// Default PES seeds based on diagnosis (RD can edit any field)
function seedPES(state, calc) {
  if (!state.diagnosis) return { problem: "", etiology: "", signs: "" };
  const dx = state.diagnosis.id;
  const problem = DX_PROBLEM_DEFAULT[dx] || "knowledgeDeficit";
  let etiology = "", signs = "";
  if (dx === "t2dm") {
    etiology = "limited carbohydrate-counting knowledge and irregular meal patterns";
    signs = state.labs?.toLowerCase().includes("hba1c")
      ? `${state.labs.match(/HbA1c\s*[\d.]+%?/i)?.[0] || "elevated HbA1c"} and reported skipped meals`
      : "elevated HbA1c and reported skipped meals";
  } else if (dx === "prediabetes") {
    etiology = "habitual intake of refined carbohydrate and sugar-sweetened beverages with limited awareness of glycemic impact";
    signs = state.labs?.toLowerCase().includes("hba1c")
      ? `${state.labs.match(/HbA1c\s*[\d.]+%?/i)?.[0] || "HbA1c in prediabetes range"} and reported intake patterns`
      : "HbA1c 5.7–6.4% and reported intake patterns";
  } else if (dx === "ckd") {
    etiology = "intake exceeding KDOQI-recommended protein restriction for non-dialysis CKD";
    signs = "dietary recall demonstrating protein intake above target g/kg/day";
  } else if (dx === "obesity") {
    etiology = "habitual energy intake above estimated needs and limited portion-control strategies";
    signs = `BMI in obese range and reported intake exceeding estimated TDEE`;
  } else if (dx === "wound") {
    etiology = "elevated metabolic demand from wound healing not currently met by usual intake";
    signs = "active pressure injury and dietary recall below target protein g/kg";
  } else if (dx === "elderly") {
    etiology = "early signs of sarcopenia and reduced appetite in the context of advancing age";
    signs = "body weight trend and protein intake below ESPEN floor of 1.0 g/kg";
  } else if (dx === "healthy") {
    etiology = "limited exposure to balanced-plate and portion-control strategies";
    signs = "self-reported intake patterns";
  }
  return { problem, etiology, signs };
}

const EMPTY_PES = { problem: "", etiology: "", signs: "" };
const EMPTY_NOTE = {
  weightTarget: "",
  weightTimeline: "12 weeks",
  followUp: "4–6 weeks",
  // edited intervention lines (overrides; null = use generated)
  interventionsOverride: null,
  monitoringOverride: null,
  needsOverride: null,
};

// v1c Epic 1 — shared compute helper. Mirrors the math the Calculations zone runs
// so the summary column reads exactly the same numbers (single source of truth
// for downstream read-only consumers). Calc zone still owns display of its own
// intermediate formulas; this helper returns only the final derived numbers.
function computeAll(state, calc) {
  const ageNum = parseFloat(state.age);
  const weightKg = state.weight ? toKg(parseFloat(state.weight), state.weightUnit) : null;
  const heightCm = state.height ? toCm(parseFloat(state.height), state.heightUnit) : null;
  const sex = state.sex;
  const dxId = state.diagnosis?.id;
  const ready = Number.isFinite(ageNum) && Number.isFinite(weightKg) && Number.isFinite(heightCm)
             && (sex === "F" || sex === "M");

  const method = ENERGY_METHODS.find(m => m.id === calc.energyMethod) || ENERGY_METHODS[0];
  const computedBMR = ready ? method.fn(weightKg, heightCm, ageNum, sex) : null;
  const bmr = calc.bmrOverride != null ? calc.bmrOverride : computedBMR;
  const activityRow = ACTIVITY.find(a => a.id === state.activity);
  const computedActivity = activityRow?.mult ?? null;
  const activityMult = calc.activityMultOverride != null ? calc.activityMultOverride : computedActivity;
  const stress = calc.stressFactor;
  const computedTDEE = (Number.isFinite(bmr) && Number.isFinite(activityMult)) ? bmr * activityMult * stress : null;
  const tdee = calc.tdeeOverride != null ? calc.tdeeOverride : computedTDEE;
  const caloricAdj = CALORIC_ADJUSTMENTS.find(c => c.id === calc.caloricAdjustment) || CALORIC_ADJUSTMENTS[2];
  const energyNeeds = Number.isFinite(tdee) ? applyModifiers("energy", tdee + caloricAdj.value, state) : null;
  const energyPerKg = (Number.isFinite(energyNeeds) && Number.isFinite(weightKg) && weightKg > 0) ? energyNeeds / weightKg : null;

  const dxDefaults = ready && dxId ? proteinDefaultsFor(dxId, ageNum, state.activity) : null;
  const wb = resolveProteinWeight({ weightKg, heightCm, sex }, calc, dxDefaults, state);
  const proteinWeight = wb.proteinWeight, ibw = wb.ibw, abw = wb.abw;
  const proteinBasisLabel = wb.basisLabel;
  const range = dxDefaults ? applyModifiers("protein", { low: dxDefaults.low, high: dxDefaults.high, point: dxDefaults.point }, state) : null;
  const computedBasis = range ? (Number.isFinite(range.point) ? range.point : (range.low + range.high) / 2) : null;
  const proteinBasis = calc.proteinBasisOverride != null ? calc.proteinBasisOverride : computedBasis;
  const proteinG = (Number.isFinite(proteinBasis) && Number.isFinite(proteinWeight)) ? proteinBasis * proteinWeight : null;

  let computedFluid = null, fluidLow = null, fluidHigh = null;
  if (calc.fluidMethod === "perKcal" && Number.isFinite(energyNeeds)) computedFluid = energyNeeds;
  else if (calc.fluidMethod === "perKg" && Number.isFinite(weightKg)) {
    fluidLow = weightKg * 30; fluidHigh = weightKg * 35;
    computedFluid = (fluidLow + fluidHigh) / 2;
  }
  const fluid = calc.fluidValueOverride != null ? calc.fluidValueOverride : computedFluid;

  return {
    ready, ageNum, weightKg, heightCm, sex, dxId,
    energyNeeds, energyPerKg, tdee, bmr,
    proteinG, proteinBasis, proteinBasisLabel, proteinWeight, ibw, abw, dxDefaults,
    fluid, fluidMethod: calc.fluidMethod,
  };
}

window.RD = {
  DIAGNOSES, TIER_LABEL, TIER_HINT, COMORBIDITIES, ACTIVITY, FOOD_SECURITY, dxShort,
  EMPTY_STATE, SAMPLE_PATIENT, SAMPLE_PATIENTS, pickSamplePatient, EMPTY_CALC,
  CITATIONS, ENERGY_METHODS, CALORIC_ADJUSTMENTS, FLUID_METHODS,
  computeBMI, bmiStatus, toCm, toKg,
  bmrMifflin, bmrHarrisBenedict, mifflinFormulaText, harrisFormulaText,
  proteinDefaultsFor, devineIBW, adjustedBodyWeight,
  MODIFIERS, activeModifiers, applyModifiers, modifierNoteLines,
  WEIGHT_BASES, defaultBasisFor, resolveProteinWeight,
  computeAll,
};

window.RD = Object.assign(window.RD || {}, {
  PROBLEMS, DX_PROBLEM_DEFAULT, DX_INTERVENTIONS, COMORBIDITY_INTERVENTIONS,
  FOOD_SECURITY_INTERVENTIONS, DX_MONITORING, PES_HELP,
  seedPES, EMPTY_PES, EMPTY_NOTE,
});
