// Copilot actions — apply intents to state.
// Pure-ish: takes intents + current state, returns { patches, calcPatches, replies }.
// `replies` is an array of assistant messages to append.

(function () {
  const { DIAGNOSES, ACTIVITY, ENERGY_METHODS } = window.RD;

  function dxLabel(id) { const d = DIAGNOSES.find(x => x.id === id); return d ? d.label : id; }
  function actLabel(id) { const a = ACTIVITY.find(x => x.id === id); return a ? a.label : id; }

  // Apply a list of `set` intents to a fresh state patch object.
  // Skip ambiguous ones — they're handled separately as disambiguation prompts.
  function applySetIntents(intents, state, sessionMem) {
    const patch = {};
    const ambigs = [];
    let summary = [];

    for (const it of intents) {
      if (it.kind !== "set") continue;
      if (it.ambig) {
        // Ambig — only ambiguate if no session memory has resolved it.
        if (it.field === "weight" && sessionMem.weightUnit) {
          patch.weight = String(it.value);
          patch.weightUnit = sessionMem.weightUnit;
          summary.push(`weight ${it.value} ${sessionMem.weightUnit}`);
        } else {
          ambigs.push(it);
        }
        continue;
      }
      switch (it.field) {
        case "age":
          patch.age = it.value;
          summary.push(`age ${it.value}`);
          break;
        case "sex":
          patch.sex = it.value;
          summary.push(`sex ${it.value === "F" ? "female" : it.value === "M" ? "male" : "other"}`);
          break;
        case "weight":
          patch.weight = String(it.value);
          if (it.unit) patch.weightUnit = it.unit;
          summary.push(`weight ${it.value} ${it.unit || ""}`.trim());
          break;
        case "height":
          patch.height = String(it.value);
          if (it.unit) patch.heightUnit = it.unit;
          summary.push(`height ${it.value} ${it.unit || ""}`.trim());
          break;
        case "diagnosis": {
          const dx = DIAGNOSES.find(d => d.id === it.value);
          if (dx) {
            patch.diagnosis = dx;
            summary.push(`diagnosis ${dx.label}${it.stage ? ` stage ${it.stage}` : ""}`);
            if (it.stage) patch.ckdStage = it.stage;
          }
          break;
        }
        case "comorbidities": {
          const existing = state.comorbidities || [];
          const merged = [...new Set([...existing, ...it.value])];
          patch.comorbidities = merged;
          summary.push(`comorbidities ${it.value.join(", ")}`);
          break;
        }
        case "activity":
          patch.activity = it.value;
          summary.push(`activity ${actLabel(it.value)}`);
          break;
        case "weightChange":
          patch.weightChange = (state.weightChange ? state.weightChange + ". " : "") + describeWeightLoss(it.value);
          summary.push("weight change noted");
          break;
        case "labs": {
          // it.value is an array of canonical "Label value[unit]" strings.
          const existing = (state.labs || "").trim();
          const existingList = existing ? existing.split(/,\s*|\n+/).map(s => s.trim()).filter(Boolean) : [];
          // Replace any existing entry with same lab name (case-insensitive prefix match).
          const merged = [...existingList];
          for (const newLab of it.value) {
            const labName = newLab.split(" ")[0].toLowerCase();
            const idx = merged.findIndex(x => x.toLowerCase().startsWith(labName + " "));
            if (idx >= 0) merged[idx] = newLab;
            else merged.push(newLab);
          }
          patch.labs = merged.join(", ");
          summary.push(`labs ${it.value.join(", ")}`);
          break;
        }
        case "foodSecurity":
          patch.foodSecurity = it.value;
          summary.push(`food security: ${it.value}`);
          break;
      }
    }

    return { patch, ambigs, summary };
  }

  function describeWeightLoss(wl) {
    if (wl.unspecified) return wl.text || "Unintentional weight loss noted";
    let s = `Lost ${wl.amount}${wl.unit || ""}`;
    if (wl.period) s += ` over ${wl.period}`;
    return s;
  }

  // Build a friendly summary message after applying a bulk patient description.
  function summaryReply(summary) {
    if (!summary.length) return null;
    return {
      role: "assistant",
      kind: "text",
      text: "Got it — populated the chart with " + summary.join(", ") + ".",
    };
  }

  // Disambiguation messages — Dial 1
  function ambigReplies(ambigs) {
    return ambigs.map(it => {
      if (it.field === "weight") {
        return {
          role: "assistant",
          kind: "chips",
          text: `Got it. Weight ${it.value} — pounds or kilograms?`,
          chips: it.candidates.map(c => ({
            label: c.label,
            action: { kind: "resolve", field: "weight", unit: c.unit, value: it.value },
          })),
        };
      }
      if (it.field === "diagnosis") {
        return {
          role: "assistant",
          kind: "chips",
          text: "Which CKD stage?",
          chips: it.candidates.map(c => ({
            label: c.label,
            action: { kind: "resolve", field: "diagnosis", value: "ckd", stage: c.stage },
          })),
        };
      }
      if (it.field === "activity") {
        return {
          role: "assistant",
          kind: "chips",
          text: "Activity level?",
          chips: it.candidates.map(c => ({
            label: c.label,
            action: { kind: "resolve", field: "activity", value: c.id },
          })),
        };
      }
      if (it.field === "lab") {
        return {
          role: "assistant",
          kind: "chips",
          text: `Got it. ${it.value} — is this an HbA1c, eGFR, or something else?`,
          chips: it.candidates.map(c => ({
            label: c.name,
            action: { kind: "resolve", field: "lab", labName: c.name, suffix: c.suffix, value: it.value },
          })),
        };
      }
      return null;
    }).filter(Boolean);
  }

  // Method change reply
  function methodReply(method) {
    const lbl = method === "msj" ? "Mifflin-St Jeor" : "Harris-Benedict";
    return { role: "assistant", kind: "text", text: `Switched to ${lbl}. Energy needs recalculating.` };
  }

  // Calc-request reply (after applying)
  function calcReply(what, state) {
    const ready = state.age && state.weight && state.height && state.sex;
    if (!ready) {
      const missing = [];
      if (!state.age) missing.push("age");
      if (!state.sex) missing.push("sex");
      if (!state.height) missing.push("height");
      if (!state.weight) missing.push("weight");
      return {
        role: "assistant",
        kind: "text",
        text: `I can run that once you give me ${missing.join(", ")}.`,
      };
    }
    if (what === "all") return { role: "assistant", kind: "text", text: "Energy, protein, and fluids are calculated and visible in the Calculation Engine zone." };
    return { role: "assistant", kind: "text", text: `${cap(what)} calculated — see the ${cap(what)} card on the right.` };
  }

  function cap(s) { return s.charAt(0).toUpperCase() + s.slice(1); }

  // Doc-request reply
  function docReply(what) {
    const labels = { pes: "PES statement", interventions: "interventions", monitoring: "monitoring plan", note: "note", all: "documentation" };
    return { role: "assistant", kind: "text", text: `Drafted the ${labels[what] || what}. It's in the Documentation Drafts zone — review and edit there.` };
  }

  // Explanation library — short, citation-anchored.
  const EXPLANATIONS = {
    energy: {
      text: "Mifflin-St Jeor estimates resting energy expenditure (BMR) from age, sex, height, and weight. We multiply by an activity factor for total daily needs (TDEE), then apply a stress factor and any caloric adjustment. Each step is shown in the Energy card.",
      cite: "Mifflin MD, et al. Am J Clin Nutr. 1990;51(2):241–7.",
    },
    activity: {
      text: "The activity multiplier scales BMR to estimate total daily energy expenditure. Sedentary 1.2 · Lightly Active 1.375 · Moderately Active 1.55 · Very Active 1.725 · Extra Active 1.9. Pick the one that best matches typical weekly activity.",
      cite: null,
    },
    protein: {
      text: "Protein basis varies by diagnosis. CKD non-dialysis 0.55–0.60 g/kg (KDOQI). T2DM 1.0–1.5 g/kg actual body weight. Wound healing 1.2–1.5 g/kg. Healthy adults 0.8–1.0 g/kg, with a 1.0 g/kg floor at age ≥65. Obesity uses adjusted body weight by default.",
      cite: "KDOQI 2020; Academy Diabetes EBNPG; NPIAP; ESPEN Geriatrics.",
    },
    abw: {
      text: "Adjusted body weight = IBW + 0.25 × (actual − IBW). Used in obesity to avoid overestimating protein and fluid needs from excess adipose tissue. Applied automatically when BMI ≥30 with an obesity diagnosis; toggleable in the Protein card.",
      cite: "Academy Adult Weight Management EBNPG.",
    },
    bmi: {
      text: "BMI = weight (kg) ÷ height (m)². It's a screening signal, not a diagnosis. Below 16 = severe underweight, ≥40 = severe obesity — both trigger malnutrition-criteria flags here. Useful for trend, less useful in athletes or older adults with sarcopenia.",
      cite: "ASPEN/Academy Consensus on Adult Malnutrition.",
    },
    citations: {
      text: "I cite from a fixed library: Mifflin-St Jeor (1990), KDOQI 2020 CKD, Academy Adult Weight Management EBNPG, Academy Diabetes EBNPG, NPIAP wound, ESPEN Geriatrics, Academy Older Adults position, ASPEN/Academy Consensus on Adult Malnutrition, Academy IDNT Reference Manual. If a recommendation is general principle, I say so.",
      cite: null,
    },
    general: {
      text: "Ask me to explain anything specific — protein, activity factor, BMR, BMI, adjusted body weight, citations.",
      cite: null,
    },
  };

  function explainReply(topic) {
    const e = EXPLANATIONS[topic] || EXPLANATIONS.general;
    return {
      role: "assistant",
      kind: "text",
      text: e.text,
      cite: e.cite || null,
    };
  }

  // Meta replies
  const META = {
    privacy: "This is a prototype. Nothing is saved or transmitted — all chart data lives in your browser tab and disappears when you close it. Don't enter real PHI.",
    citations: "I cite from a fixed library of guidelines (KDOQI 2020, Academy EBNPGs, ASPEN, NPIAP, ESPEN, Mifflin-St Jeor 1990). I won't invent references — if something is general principle, I'll say so.",
    methods: "Energy uses Mifflin-St Jeor (default) or Harris-Benedict, multiplied by activity and stress factors. Protein basis is diagnosis-driven with a geriatric floor. Fluids use 1 mL/kcal or 30–35 mL/kg. Every calculation is editable inline and shows its formula.",
    general: "I'm here to help draft this patient's chart. Ask me to update fields, run calculations, draft documentation, or explain a number. The About panel (top-right of this rail) has the full picture.",
  };

  function metaReply(topic) {
    return { role: "assistant", kind: "text", text: META[topic] || META.general };
  }

  // Out-of-scope
  function oosReply() {
    return {
      role: "assistant",
      kind: "text",
      text: "I'm focused on this patient's chart for now. For general clinical guidance, check your usual references.",
    };
  }

  // Noop
  function noopReply(text) {
    return {
      role: "assistant",
      kind: "text",
      text: "I didn't quite catch a chart update or question in that. Try \"set weight to 187 lb\", \"calculate energy needs\", or \"why 0.6 g/kg protein?\"",
    };
  }

  window.RD_Actions = {
    applySetIntents,
    ambigReplies,
    summaryReply,
    methodReply,
    calcReply,
    docReply,
    explainReply,
    metaReply,
    oosReply,
    noopReply,
    EXPLANATIONS,
  };
})();
