/* NutriDMS, Compliance rule wizards (Nutrition · Ingredient · Health Tag · Allergen)
   Large centered two-pane popup: step-by-step builder on the left, a LIVE preview
   on the right that simulates, in real time, exactly what the rule will trigger
   and which recipes/ingredients it affects across the library.
   Mounted once at root; opened via openCompliance({type, mode, row, onSaved}).
   Read-only when mode === "view" or the role can't edit. */

const { useState: useCwState, useMemo: useCwMemo, useEffect: useCwEffect } = React;

/* ───────── Shared atoms ───────── */

/* Stepper header */
function CwSteps({ steps, active, onJump }) {
  return (
    <div className="cw-steps">
      {steps.map((s, i) => (
        <React.Fragment key={s}>
          {i > 0 && <span className={`cw-step-line ${i <= active ? "done" : ""}`} />}
          <button type="button" className={`cw-step ${i < active ? "done" : i === active ? "current" : ""}`} onClick={() => onJump && i <= active && onJump(i)} disabled={!onJump || i > active}>
            <span className="cw-step-dot">{i < active ? <Icon name="check" size={13} stroke={2.6} /> : <span className="cw-step-ring" />}</span>
            <span className="cw-step-label">{s}</span>
          </button>
        </React.Fragment>
      ))}
    </div>
  );
}

/* Loraa step guidance, a short, branded "here's what to do" line at the top of each step. */
function CwGuide({ children }) {
  return (
    <div className="cw-guide">
      <span className="cu-loraa-logo sm"><img src="assets/loraa-logo.png" alt="Loraa" /></span>
      <p>{children}</p>
    </div>
  );
}

/* Optional-field marker */
function Opt() { return <em className="cw-opt">(optional)</em>; }

/* ═══════════ Loraa rule copilot (Enterprise-only) ═══════════
   An ACTIVE assistant pinned to the right pane across every step of every rule
   wizard. It re-reads the draft + live simulation on each render and surfaces
   contextual, prioritised guidance with jump-to-step actions and an Ask row.
   Gated: only enterprise subscribers get the live copilot; everyone else sees a
   locked upsell card. */

function cwIsEnterprise() {
  try { return (typeof mpPlan === "function" ? mpPlan() : "professional") === "enterprise"; } catch (e) { return false; }
}

/* Build prioritised, draft-aware insights. Returns [{tone, icon, title, body, step}] */
function cwLoraaInsights(type, draft, sim) {
  const out = [];
  const sev = draft.severity || "Minimal";
  const hard = /^(severe|critical)$/i.test(sev);
  const limits = (draft.limits || []).filter((l) => l.nutrient);
  const ings = (draft.ingredients || []).filter(Boolean);

  // Identity coverage (all types)
  if (!draft.name || !String(draft.name).trim()) {
    out.push({ tone: "warn", icon: "type", title: "Name this rule", body: "Reviewers and the audit log key off the rule name, give it something specific.", step: 1 });
  }

  if (type === "nutrition") {
    if (!limits.length) {
      out.push({ tone: "warn", icon: "sliders-horizontal", title: "Add a threshold", body: "A nutrition rule does nothing until you set at least one Min or Max. I'll simulate impact the moment you do.", step: 1 });
    } else if (sim) {
      const pct = sim.tested ? Math.round((sim.flagged.length / sim.tested) * 100) : 0;
      if (sim.flagged.length === 0) {
        out.push({ tone: "info", icon: "search", title: "Catches nothing today", body: `No published recipe breaches these limits. That's fine for a guardrail, but if you meant to catch outliers, tighten the threshold.`, step: 1 });
      } else if (pct >= 40) {
        out.push({ tone: "warn", icon: "radar", title: "Wide blast radius", body: `This flags ${sim.flagged.length} of ${sim.tested} recipes (${pct}%). ${hard ? "At " + sev + " severity that blocks all of them until fixed." : "Confirm that's intended before activating."}`, step: 1 });
      } else {
        out.push({ tone: "good", icon: "circle-check", title: "Looks well-targeted", body: `${sim.flagged.length} of ${sim.tested} recipes (${pct}%) flag, a focused set you can action.`, step: 1 });
      }
    }
    if (limits.length && !hard) {
      out.push({ tone: "info", icon: "shield", title: "Severity is advisory", body: `At ${sev}, recipes are warned but not blocked. Raise to Severe/Critical to gate submission.`, step: 1 });
    }
  }

  if (type === "ingredient") {
    if (!ings.length) {
      out.push({ tone: "warn", icon: "leaf", title: "Pick ingredients to watch", body: "Choose the ingredients this rule should catch, I'll list every recipe that uses them on the right.", step: 1 });
    }
    const alts = (draft.alternatives || draft.alts || []).filter((a) => a && (a.ing || a.name));
    if (ings.length && !alts.length) {
      out.push({ tone: "info", icon: "repeat", title: "Offer a swap", body: "Add an approved alternative with its nutrient target so cooks know exactly what to substitute, that's what turns a block into a fix.", step: 1 });
    } else if (alts.length) {
      out.push({ tone: "good", icon: "repeat", title: `${alts.length} alternative${alts.length === 1 ? "" : "s"} ready`, body: "Cooks will see these swaps inline when a recipe trips this rule.", step: 1 });
    }
    if (sim && sim.flagged.length) {
      out.push({ tone: "warn", icon: "radar", title: `${sim.flagged.length} recipe${sim.flagged.length === 1 ? "" : "s"} affected`, body: `Currently use the watched ingredient${ings.length === 1 ? "" : "s"}. ${hard ? "These will be blocked at " + sev + "." : "These get a " + sev.toLowerCase() + " warning."}`, step: 1 });
    }
  }

  if (type === "allergen") {
    if (!ings.length) {
      out.push({ tone: "warn", icon: "alert-triangle", title: "Link the carriers", body: "Tag every ingredient that carries this allergen, any recipe with one gets flagged automatically.", step: 1 });
    }
    if (!hard) {
      out.push({ tone: "warn", icon: "shield-alert", title: "Allergens usually block", body: `You're at ${sev}. Allergen rules are typically Severe or Critical so unsafe recipes can't ship, consider raising it.`, step: 1 });
    } else {
      out.push({ tone: "good", icon: "shield-check", title: "Hard block set", body: `At ${sev}, any recipe containing a linked ingredient is blocked until resolved.`, step: 1 });
    }
  }

  if (type === "healthtag") {
    const low = (draft.low || []).length, mod = (draft.mod || []).length, high = (draft.high || []).length;
    const conds = (draft.conditions || []).length;
    out.push({ tone: "info", icon: "info", title: "Tags steer, they don't block", body: "Health tags shape scoring and patient matching rather than hard-flagging recipes, there's no blast radius to simulate.", step: 1 });
    if (!conds) {
      out.push({ tone: "warn", icon: "heart-pulse", title: "Attach conditions", body: "Link the conditions this tag serves so the right patients get matched to meals carrying it.", step: 1 });
    }
    if (!(low + mod + high)) {
      out.push({ tone: "warn", icon: "list", title: "Map some nutrients", body: "Sort a few nutrients into keep-low / moderate / encourage so meals can be scored against this tag.", step: 2 });
    } else if (!high) {
      out.push({ tone: "info", icon: "trending-up", title: "Nothing to encourage", body: "You've set restrictions but no encouraged nutrients, adding a couple makes scoring more balanced.", step: 2 });
    } else {
      out.push({ tone: "good", icon: "circle-check", title: "Balanced map", body: `${low} low · ${mod} moderate · ${high} encouraged, meals can be scored cleanly.`, step: 2 });
    }
  }

  return out.slice(0, 4);
}

const CW_LORAA_ASKS = {
  nutrition: [
    { q: "What severity should I use?", a: "Use Minimal/Low for nudges, Moderate/Warning to surface issues in review, and Severe/Critical only when a breach should block submission outright." },
    { q: "How tight should the limit be?", a: "Watch the simulation: aim for a threshold that flags the genuine outliers (a handful of recipes), not zero and not half the library." },
    { q: "Min, Max, or both?", a: "Max caps a nutrient (sodium, sugar), Min enforces a floor (protein, fibre). Set both to define an acceptable band." },
  ],
  ingredient: [
    { q: "Do I need an alternative?", a: "Not required, but recommended, a named swap with a nutrient target turns a hard block into a one-click fix for cooks." },
    { q: "How does this link to allergens?", a: "If the watched ingredient also carries an allergen, link it in the Allergen Table so both rules fire together." },
    { q: "Will existing recipes break?", a: "The live panel lists exactly which approved recipes use the ingredient today, that's your migration list." },
  ],
  allergen: [
    { q: "What severity for allergens?", a: "Almost always Severe or Critical, an allergen slip is a safety event, so blocking submission is the safe default." },
    { q: "Do I list every ingredient?", a: "Link every carrier you know of. Recipes are matched on these, so a missing carrier means a missed flag." },
  ],
  healthtag: [
    { q: "Why no recipe simulation?", a: "Tags influence scoring and patient matching, not pass/fail, so there's no flag count to preview, only the recommendation map." },
    { q: "Keep-low vs encourage?", a: "Keep-low nutrients pull a meal's score down for this tag; encouraged nutrients lift it. Moderate sits neutral." },
    { q: "Where do conditions come from?", a: "From Health Conditions, managing them there keeps this list consistent across every rule." },
  ],
};

function CwLoraa({ draft, setStep }) {
  const type = ({ "Nutrition": "nutrition", "Ingredient": "ingredient", "Allergen": "allergen", "Health Tag": "healthtag" })[draft.type] || "nutrition";
  const [, bumpPlan] = React.useState(0);
  React.useEffect(() => {
    const h = () => bumpPlan((n) => n + 1);
    window.addEventListener("nutridms-mealprograms", h);
    return () => window.removeEventListener("nutridms-mealprograms", h);
  }, []);
  const enterprise = cwIsEnterprise();
  let loraaToast = null; try { loraaToast = useApp().toast; } catch (e) {}
  const [ask, setAsk] = React.useState(null);
  const sim = useCwMemo(() => {
    if (!enterprise || type === "healthtag") return null;
    try {
      const d = { type: draft.type, name: draft.name, severity: draft.severity, limits: (draft.limits || []).filter((l) => l.nutrient), ingredients: (draft.ingredients || []).filter(Boolean) };
      return (typeof compSimulateRule === "function") ? compSimulateRule(d) : null;
    } catch (e) { return null; }
  }, [enterprise, type, JSON.stringify(draft)]);

  if (!enterprise) {
    return (
      <div className="cw-loraa locked">
        <div className="cw-loraa-head">
          <span className="cu-loraa-logo sm cw-loraa-locklogo"><img src="assets/loraa-logo.png" alt="Loraa" /></span>
          <div className="cw-loraa-head-t"><strong>Loraa rule copilot</strong><span className="cw-loraa-ent"><Icon name="lock" size={10} /> Enterprise</span></div>
        </div>
        <p className="cw-loraa-lockbody">Live, rule-aware coaching, threshold tuning, blast-radius checks and swap suggestions as you build, is part of the <strong>Enterprise</strong> plan.</p>
        <ul className="cw-loraa-lockfeat">
          <li><Icon name="radar" size={13} /> Real-time impact simulation</li>
          <li><Icon name="lightbulb" size={13} /> Contextual fixes per step</li>
          <li><Icon name="message-circle" size={13} /> Ask Loraa about any rule</li>
        </ul>
        <button className="cw-loraa-upgrade" onClick={() => { loraaToast && loraaToast("Loraa rule copilot is part of the Enterprise plan, manage it in Settings › Subscription & Features."); }}>
          <Icon name="arrow-up-circle" size={14} /> Upgrade to Enterprise
        </button>
      </div>
    );
  }

  const insights = cwLoraaInsights(type, draft, sim);
  const asks = CW_LORAA_ASKS[type] || [];
  return (
    <div className="cw-loraa">
      <div className="cw-loraa-head">
        <span className="cu-loraa-logo sm"><img src="assets/loraa-logo.png" alt="Loraa" /></span>
        <div className="cw-loraa-head-t"><strong>Loraa rule copilot</strong><span className="cw-loraa-status"><span className="cw-loraa-dot" /> Reviewing as you build</span></div>
        <span className="cw-loraa-ent on"><Icon name="zap" size={10} /> Enterprise</span>
      </div>
      <div className="cw-loraa-insights">
        {insights.map((it, i) => (
          <div key={i} className={`cw-loraa-ins ${it.tone}`}>
            <span className="cw-loraa-ins-ic"><Icon name={it.icon} size={14} /></span>
            <div className="cw-loraa-ins-b">
              <strong>{it.title}</strong>
              <p>{it.body}</p>
              {it.step != null && setStep ? <button className="cw-loraa-jump" onClick={() => setStep(it.step)}>Go fix this <Icon name="arrow-right" size={12} /></button> : null}
            </div>
          </div>
        ))}
      </div>
      {asks.length > 0 && (
        <div className="cw-loraa-ask">
          <div className="cw-loraa-ask-chips">
            {asks.map((a, i) => (
              <button key={i} className={`cw-loraa-chip ${ask === i ? "on" : ""}`} onClick={() => setAsk(ask === i ? null : i)}>{a.q}</button>
            ))}
          </div>
          {ask != null && asks[ask] && (
            <div className="cw-loraa-answer"><span className="cu-loraa-logo sm"><img src="assets/loraa-logo.png" alt="" /></span><p>{asks[ask].a}</p></div>
          )}
        </div>
      )}
    </div>
  );
}

/* Severity tag used across the live preview */
function CwSev({ sev }) {
  const tone = (typeof compSeverityTone === "function") ? compSeverityTone(sev) : "neutral";
  return <span className={`cw-sevtag ${tone}`}>{sev}</span>;
}

/* Step 1, input method (shared). onScratch advances into the step-by-step builder. */
function CwMethod({ noun, onScratch, readOnly, bulkType, onImported }) {
  const { toast } = useApp();
  const fileRef = React.useRef(null);
  const [busy, setBusy] = React.useState(false);
  const proceed = () => { if (onScratch) onScratch(); };
  const supportsBulk = bulkType === "nutrition" && !readOnly;
  const downloadTemplate = async () => {
    try {
      await compDownloadRuleTemplate();
      toast && toast("Nutrient rule template downloaded");
    } catch (error) {
      toast && toast((error && error.message) || "The rule template could not be downloaded.");
    }
  };
  const importFile = async (event) => {
    const input = event && event.target;
    const file = input && input.files && input.files[0];
    if (!file) return;
    setBusy(true);
    try {
      const result = await compBulkUploadRuleFile(file);
      toast && toast((result.imported || 0) + " nutrient rule" + (result.imported === 1 ? "" : "s") + " imported");
      onImported && onImported();
    } catch (error) {
      const first = error && error.data && Array.isArray(error.data.rows) && error.data.rows[0];
      const rowMessage = first && Array.isArray(first.errors) ? " Row " + first.row + ": " + first.errors.join("; ") : "";
      toast && toast(((error && error.message) || "The rules were not imported.") + rowMessage);
    } finally {
      setBusy(false);
      if (input) input.value = "";
    }
  };
  return (
    <div className="cw-pane">
      <CwGuide>{supportsBulk ? "Import a validated CSV to bulk-create nutrient rules, or build one step by step. Every imported row is checked before anything is saved." : "Build this " + noun + " step by step, I'll preview what it affects as you go."}</CwGuide>
      <h3 className="cw-step-title">{readOnly ? "View rule details" : "How do you want to start?"}</h3>
      {supportsBulk && (
        <>
          <div className="cw-csv-row">
            <Icon name="file-text" size={18} />
            <span>Download the live CSV template. Its columns exactly match the tenant-scoped importer.</span>
            <button className="btn secondary sm" disabled={busy} onClick={downloadTemplate}><Icon name="download" size={14} /> Download Template</button>
          </div>
          <div className="cw-dropzone">
            <span className="cw-drop-ic"><Icon name="file-up" size={26} /></span>
            <p>Choose a completed NutriDMS nutrient-rule CSV file.</p>
            <input type="file" accept=".csv,text/csv" ref={fileRef} style={{ display: "none" }} onChange={importFile} />
            <button className="btn primary sm" disabled={busy} onClick={() => fileRef.current && fileRef.current.click()}>
              <Icon name={busy ? "loader" : "upload"} size={14} className={busy ? "spin" : ""} /> {busy ? "Importing…" : "Choose File"}
            </button>
            <span className="cw-drop-hint">Maximum 200 rows and 1 MB. Import is all-or-nothing.</span>
          </div>
        </>
      )}
      <button className="cw-scratch" onClick={proceed}><span className="cw-scratch-ic"><Icon name={readOnly ? "eye" : "plus"} size={16} /></span><div><strong>{readOnly ? "Continue to rule" : "Create from scratch"}</strong><span>{readOnly ? "Review its thresholds and scope" : "Build your " + noun + " step by step"}</span></div><Icon name="arrow-right" size={16} className="cw-scratch-arrow" /></button>
    </div>
  );
}

/* Activate toggle row */
function CwActivate({ on, set, disabled }) {
  return (
    <div className="comp-activate">
      <div><strong>Do you want to activate this Rule?</strong><span>Once activated it applies to ingredients and recipes selected</span></div>
      <button type="button" className={`la-toggle ${on ? "on" : ""}`} disabled={disabled} onClick={() => set(!on)}><span className="la-knob" /></button>
    </div>
  );
}

/* Photo picker, stock library (searchable, by category) + upload your own.
   Opens as a small modal over the wizard. onPick(url) assigns; onClear removes override. */
function CwPhotoPicker({ tag, current, onPick, onClose }) {
  const { toast } = useApp();
  const [cat, setCat] = useCwState("All");
  const [q, setQ] = useCwState("");
  const fileRef = React.useRef(null);
  const stock = (window.COMP_STOCK_PHOTOS || []);
  const cats = (window.COMP_STOCK_CATS || ["All"]);
  const shown = stock.filter((p) => (cat === "All" || p.cat === cat) && (!q || p.cat.toLowerCase().includes(q.toLowerCase())));
  const onFile = (e) => {
    const f = e.target.files && e.target.files[0];
    if (!f) return;
    if (!/^image\//.test(f.type)) { toast && toast("Please choose an image file"); return; }
    const reader = new FileReader();
    reader.onload = () => { onPick(reader.result); toast && toast(`Custom photo set for ${tag}`); };
    reader.readAsDataURL(f);
  };
  return (
    <div className="cw-pp-overlay" onMouseDown={(e) => { if (e.target === e.currentTarget) onClose(); }}>
      <div className="cw-pp" role="dialog" aria-label={`Choose a photo for ${tag}`}>
        <div className="cw-pp-head">
          <div className="cw-pp-head-t"><strong>Choose a photo</strong><span>for <b>{tag}</b></span></div>
          <button className="cw-pp-x" onClick={onClose} aria-label="Close"><Icon name="x" size={16} /></button>
        </div>
        <div className="cw-pp-toolbar">
          <label className="cw-pp-upload">
            <input type="file" accept="image/*" ref={fileRef} onChange={onFile} />
            <Icon name="upload" size={14} /> Upload your own
          </label>
          <div className="cw-pp-search"><Icon name="search" size={14} /><input value={q} onChange={(e) => setQ(e.target.value)} placeholder="Search stock photos" /></div>
        </div>
        <div className="cw-pp-cats">
          {cats.map((c) => <button key={c} className={`cw-pp-cat ${cat === c ? "on" : ""}`} onClick={() => setCat(c)}>{c}</button>)}
        </div>
        <div className="cw-pp-grid">
          {current && (
            <button className="cw-pp-tile cw-pp-current on" onClick={() => onPick(current)} title="Current photo">
              <span className="cw-pp-img" style={{ backgroundImage: `url("${current}")` }} />
              <span className="cw-pp-cur-badge"><Icon name="check" size={11} stroke={3} /> Current</span>
            </button>
          )}
          {shown.map((p, i) => (
            <button key={i} className={`cw-pp-tile ${p.url === current ? "on" : ""}`} onClick={() => onPick(p.url)} title={p.cat}>
              <span className="cw-pp-img" style={{ backgroundImage: `url("${p.thumb}")` }} />
            </button>
          ))}
        </div>
        <div className="cw-pp-foot">
          <span>{shown.length} stock photos</span>
          <button className="btn secondary sm" onClick={onClose}>Done</button>
        </div>
      </div>
    </div>
  );
}

/* Map-to-health-tags photo grid */
function CwMapTags({ value, onChange, disabled }) {
  const tags = Object.keys(COMP_TAG_PHOTOS);
  const [, bump] = React.useState(0);
  const [picker, setPicker] = React.useState(null); // tag being re-photographed
  React.useEffect(() => {
    const h = () => bump((n) => n + 1);
    window.addEventListener("nutridms-tag-photos", h);
    return () => window.removeEventListener("nutridms-tag-photos", h);
  }, []);
  const toggle = (t) => { if (disabled) return; onChange(value.includes(t) ? value.filter((x) => x !== t) : [...value, t]); };
  const photo = (t) => (typeof compTagPhoto === "function" ? compTagPhoto(t) : COMP_TAG_PHOTOS[t]);
  return (
    <div className="cw-pane">
      <CwGuide>Linking tags lets this rule run automatically wherever the tag is applied. Pick any that fit, you can map several. Hover a card to swap its photo for a stock image or one of your own.</CwGuide>
      <h3 className="cw-step-title">Map this rule to health tags <Opt /></h3>
      <div className="cw-tag-grid">
        {tags.map((t) => (
          <div key={t} className={`cw-tag-card ${value.includes(t) ? "on" : ""}`}>
            <button type="button" className="cw-tag-hit" onClick={() => toggle(t)}>
              <span className="cw-tag-photo" style={{ backgroundImage: `url("${photo(t)}")` }} />
              <span className="cw-tag-label"><span className={`cw-radio ${value.includes(t) ? "on" : ""}`}>{value.includes(t) && <Icon name="check" size={11} stroke={3} />}</span>{t}</span>
            </button>
            {!disabled && (
              <button type="button" className="cw-tag-editphoto" title="Change photo" onClick={(e) => { e.stopPropagation(); setPicker(t); }}>
                <Icon name="image" size={13} /> Photo
              </button>
            )}
          </div>
        ))}
      </div>
      {picker && (
        <CwPhotoPicker
          tag={picker}
          current={photo(picker)}
          onPick={(url) => { compSetTagPhoto(picker, url); setPicker(null); }}
          onClose={() => setPicker(null)}
        />
      )}
    </div>
  );
}

/* ═══════════ LIVE PREVIEW (right pane) ═══════════
   Re-renders on every keystroke. Shows rule identity, the exact trigger logic
   (per nutrient / ingredient + severity), and a live simulation against the
   recipe library so the author sees the blast radius before activating. */

function cwLimitPhrase(l) {
  const has = (v) => v != null && String(v).trim() !== "";
  if (has(l.min) && has(l.max)) return `outside ${l.min}–${l.max}`;
  if (has(l.max)) return `above ${l.max}`;
  if (has(l.min)) return `below ${l.min}`;
  return "any value";
}
function cwNutrShort(n) { return String(n || "").replace(/\s*\(.*\)\s*$/, ""); }

function CwLiveSim({ simDraft }) {
  const res = useCwMemo(
    () => (typeof compSimulateRule === "function") ? compSimulateRule(simDraft) : { tested: 0, flagged: [] },
    [JSON.stringify(simDraft)]
  );
  const pct = res.tested ? Math.round((res.flagged.length / res.tested) * 100) : 0;
  return (
    <div className="cw-live-card">
      <div className="cw-live-card-head">
        <span className="cw-live-ic sim"><Icon name="radar" size={15} /></span>
        <div className="cw-live-card-t"><strong>Live recipe simulation</strong><span>Run against {res.tested} published recipes</span></div>
        <span className={`pill ${res.flagged.length ? "warning" : "success"}`}>{res.flagged.length ? `${res.flagged.length} affected` : "All clear"}</span>
      </div>
      <div className="cw-live-meter"><span className="cw-live-meter-fill" style={{ width: `${pct}%` }} /></div>
      <div className="cw-live-meter-cap"><strong>{res.flagged.length}</strong> of {res.tested} recipes would be flagged <span>· {pct}%</span></div>
      {res.flagged.length > 0 ? (
        <ul className="cw-live-list">
          {res.flagged.slice(0, 7).map((f, i) => (
            <li key={i}>
              <Icon name="triangle-alert" size={13} />
              <span className="cw-live-list-name">{f.name}</span>
              <span className="cw-live-list-detail">{f.detail}</span>
            </li>
          ))}
          {res.flagged.length > 7 && <li className="cw-live-more">+{res.flagged.length - 7} more recipes</li>}
        </ul>
      ) : (
        <div className="cw-live-clear"><Icon name="circle-check" size={14} /> No current recipes breach this, it guards new submissions going forward.</div>
      )}
    </div>
  );
}

function CwLivePreview({ draft }) {
  const limits = (draft.limits || []).filter((l) => l.nutrient);
  const ings = (draft.ingredients || []).filter(Boolean);
  const isHealthTag = draft.type === "Health Tag";
  const configured = limits.length || ings.length || isHealthTag;
  const simDraft = { type: draft.type, name: draft.name, severity: draft.severity, limits, ingredients: ings };

  return (
    <div className="cw-live">
      <div className="cw-live-head">
        <span className="cw-live-badge"><span className="cw-live-dot" /> Live preview</span>
        <span className="cw-live-sub">Updates as you build · simulated on real data</span>
      </div>

      {/* Identity */}
      <div className="cw-live-id">
        <div className="cw-live-id-top">
          <span className={`comp-type ${({ "Nutrition": "t-nutrition", "Health Tag": "t-healthtag", "Ingredient": "t-ingredient", "Allergen": "t-ingredient" })[draft.type] || "t-nutrition"}`}>{draft.type}</span>
          <span className={`pill ${draft.active ? "success" : "neutral"}`}>{draft.active ? "Active" : "Inactive"}</span>
        </div>
        <div className="cw-live-id-name">{draft.name || (isHealthTag ? "Untitled health tag" : "Untitled rule")}</div>
        {draft.desc ? <div className="cw-live-id-desc">{draft.desc}</div> : null}
        <div className="cw-live-id-meta">
          {draft.severity ? <span><span className="cw-live-id-k">Severity</span><CwSev sev={draft.severity} /></span> : null}
          {draft.source ? <span><span className="cw-live-id-k">Source</span>{String(draft.source).replace(" (default)", "")}</span> : null}
        </div>
      </div>

      {!configured ? (
        <div className="cw-live-empty">
          <Icon name="wand-2" size={26} />
          <p>Start building, your live trigger logic and recipe impact will appear here.</p>
        </div>
      ) : null}

      {/* Trigger logic, nutrient measurements per severity */}
      {limits.length > 0 && (
        <div className="cw-live-card">
          <div className="cw-live-card-head">
            <span className="cw-live-ic trig"><Icon name="activity" size={15} /></span>
            <div className="cw-live-card-t"><strong>What it triggers</strong><span>{limits.length} nutrient threshold{limits.length > 1 ? "s" : ""}</span></div>
          </div>
          <ul className="cw-trig-list">
            {limits.map((l, i) => (
              <li key={i} className="cw-trig">
                <span className="cw-trig-scope">{l.scope === "recipe" ? "Recipe" : "Ingredient"}</span>
                <div className="cw-trig-body">
                  <span className="cw-trig-rule">If <strong>{cwNutrShort(l.nutrient)}</strong> is <strong>{cwLimitPhrase(l)}</strong></span>
                  <span className="cw-trig-arrow"><Icon name="arrow-right" size={12} /> flag</span>
                  <CwSev sev={l.severity || draft.severity} />
                </div>
              </li>
            ))}
          </ul>
        </div>
      )}

      {/* Ingredient / allergen presence triggers */}
      {ings.length > 0 && (
        <div className="cw-live-card">
          <div className="cw-live-card-head">
            <span className="cw-live-ic ing"><Icon name="ban" size={15} /></span>
            <div className="cw-live-card-t"><strong>{draft.type === "Allergen" ? "Flags any recipe containing" : "Watched ingredients"}</strong><span>{ings.length} linked</span></div>
            {draft.severity ? <CwSev sev={draft.severity} /> : null}
          </div>
          <div className="cw-trig-chips">{ings.map((x) => <span className="cw-trig-chip" key={x}><Icon name="alert-triangle" size={11} /> {x}</span>)}</div>
          {draft.note ? <div className="cw-live-note"><Icon name="info" size={12} /> {draft.note}</div> : null}
        </div>
      )}

      {/* Alternatives (ingredient rule) */}
      {(draft.alts || []).filter((a) => a.ing).length > 0 && (
        <div className="cw-live-card">
          <div className="cw-live-card-head">
            <span className="cw-live-ic alt"><Icon name="repeat-2" size={15} /></span>
            <div className="cw-live-card-t"><strong>Suggested alternatives</strong><span>Offered when blocked</span></div>
          </div>
          <ul className="cw-alt-list">
            {draft.alts.filter((a) => a.ing).map((a, i) => (
              <li key={i}>
                <span className="cw-alt-name">{a.ing}</span>
                {a.nutrient ? <span className="cw-alt-nutr">{cwNutrShort(a.nutrient)} {cwLimitPhrase(a)}</span> : null}
                <CwSev sev={a.sev || "Minimal"} />
              </li>
            ))}
          </ul>
        </div>
      )}

      {/* Health-tag recommendation map */}
      {isHealthTag && (
        <>
          <div className="cw-live-card">
            <div className="cw-live-card-head">
              <span className="cw-live-ic trig"><Icon name="sliders-horizontal" size={15} /></span>
              <div className="cw-live-card-t"><strong>Recommendation map</strong><span>How meals are scored for “{draft.tag}”</span></div>
            </div>
            <div className="cw-rec-rows">
              <div className="cw-rec-row r"><span className="cw-rec-k">Keep low</span><div className="cw-rec-chips">{(draft.low || []).map((n) => <span key={n}>{n}</span>)}{!(draft.low || []).length && <em>—</em>}</div></div>
              <div className="cw-rec-row a"><span className="cw-rec-k">Moderate</span><div className="cw-rec-chips">{(draft.mod || []).map((n) => <span key={n}>{n}</span>)}{!(draft.mod || []).length && <em>—</em>}</div></div>
              <div className="cw-rec-row g"><span className="cw-rec-k">Encourage</span><div className="cw-rec-chips">{(draft.high || []).map((n) => <span key={n}>{n}</span>)}{!(draft.high || []).length && <em>—</em>}</div></div>
            </div>
          </div>
          {(draft.conditions || []).length > 0 && (
            <div className="cw-live-card">
              <div className="cw-live-card-head">
                <span className="cw-live-ic ing"><Icon name="heart-pulse" size={15} /></span>
                <div className="cw-live-card-t"><strong>Linked conditions</strong><span>Patients matched to this tag</span></div>
              </div>
              <div className="cw-trig-chips">{draft.conditions.map((c) => <span className="cw-trig-chip cond" key={c}>{c}</span>)}</div>
            </div>
          )}
        </>
      )}

      {/* Mapped tags */}
      {(draft.tags || []).filter((t) => COMP_TAG_PHOTOS[t]).length > 0 && (
        <div className="cw-live-card">
          <div className="cw-live-card-head">
            <span className="cw-live-ic alt"><Icon name="tags" size={15} /></span>
            <div className="cw-live-card-t"><strong>Runs with tags</strong><span>Auto-applies on these</span></div>
          </div>
          <div className="cw-trig-chips">{draft.tags.filter((t) => COMP_TAG_PHOTOS[t]).map((t) => <span className="cw-trig-chip tag" key={t}><span className="cw-trig-chip-ph" style={{ backgroundImage: `url("${(typeof compTagPhoto === "function" ? compTagPhoto(t) : COMP_TAG_PHOTOS[t])}")` }} /> {t}</span>)}</div>
        </div>
      )}

      {/* Live simulation, nutrient & ingredient rules only (health-tag has no hard flags) */}
      {!isHealthTag && (limits.length || ings.length) ? <CwLiveSim simDraft={simDraft} /> : null}
    </div>
  );
}

/* ═══════════ Nutrition rule wizard ═══════════ */
function CwNutrition({ mode, row, readOnly, onSave, onCancel, step, setStep }) {
  const steps = ["Method", "Limits & activate", "Map tags", "Review"];
  const [name, setName] = useCwState(row ? row.name : "");
  const [source, setSource] = useCwState(row ? (/custom/i.test(row.source) ? "Custom (default)" : "Regulatory") : "Custom (default)");
  const [desc, setDesc] = useCwState(row ? row.desc : "");
  const init = row && typeof compLoad === "function" ? (compLoad().limits[row.id] || []) : [];
  const [profile, setProfile] = useCwState("ingredient");
  const [ingRows, setIngRows] = useCwState(init.filter((l) => l.scope !== "recipe").length ? init.filter((l) => l.scope !== "recipe") : [blankLimit("ingredient"), blankLimit("ingredient")]);
  const [recRows, setRecRows] = useCwState(init.filter((l) => l.scope === "recipe").length ? init.filter((l) => l.scope === "recipe") : [blankLimit("recipe")]);
  const [tags, setTags] = useCwState(row ? (row.tags || []).filter((t) => COMP_TAG_PHOTOS[t]) : ["Heart Friendly"]);
  const [active, setActive] = useCwState(row ? row.status === "active" : true);

  const rows = profile === "ingredient" ? ingRows : recRows;
  const setRows = profile === "ingredient" ? setIngRows : setRecRows;
  const updRow = (i, k, v) => setRows(rs => rs.map((r, j) => j === i ? { ...r, [k]: v } : r));
  const addRow = () => setRows(rs => [...rs, blankLimit(profile)]);
  const delRow = (i) => setRows(rs => rs.filter((_, j) => j !== i));

  const allLimits = [...ingRows.map(r => ({ ...r, scope: "ingredient" })), ...recRows.map(r => ({ ...r, scope: "recipe" }))];
  const draft = { type: "Nutrition", name, desc, source, active, severity: (allLimits.find(r => r.nutrient) || {}).severity || "Moderate", limits: allLimits, tags };

  const doSave = async () => {
    const id = row ? row.id : "cr-" + Date.now();
    const saved = allLimits.filter(r => r.nutrient);
    const ruleRow = {
      id, __remote: !!(row && row.__remote),
      name: name || "Untitled rule", desc: desc || "Custom nutrition rule",
      type: "Nutrition", status: active ? "active" : "inactive",
      severity: (saved[0] && saved[0].severity) || "Minimal",
      source: source.replace(" (default)", ""), tags, updated: todayStr(),
    };
    await compSaveWizardRule("rules", ruleRow, {
      limits: saved,
      description: ruleRow.desc,
      appliesTo: ["recipes", "ingredients"],
    });
    onSave();
  };

  return (
    <CwShell title={mode === "edit" ? "Edit Nutrition Rule" : mode === "view" ? "Nutrition Rule" : "Add Nutrition Rule"}
      subtitle="Set nutrient thresholds that flag recipes and ingredients" steps={steps} step={step} setStep={setStep}
      onCancel={onCancel} onBack={step > 0 ? () => setStep(step - 1) : null}
      onNext={step < steps.length - 1 ? () => setStep(step + 1) : null}
      onSave={step === steps.length - 1 && !readOnly ? doSave : null} readOnly={readOnly}
      preview={<CwLivePreview draft={draft} />} loraa={<CwLoraa draft={draft} setStep={setStep} />}>
      {step === 0 && <CwMethod noun="rule" bulkType="nutrition" onImported={onCancel} onScratch={() => setStep(1)} readOnly={readOnly} />}
      {step === 1 && (
        <div className="cw-pane">
          <CwGuide>Name the rule, then add thresholds. Set a Min, a Max, or both, each becomes a trigger you can watch on the right.</CwGuide>
          <div className="cw-fld-row">
            <label className="comp-fld"><span>Rule Name</span><input value={name} disabled={readOnly} placeholder="e.g., Diabetic Carb Limit" onChange={e => setName(e.target.value)} /></label>
            <label className="comp-fld"><span>Source</span><select value={source} disabled={readOnly} onChange={e => setSource(e.target.value)}>{COMP_SOURCES.map(s => <option key={s}>{s}</option>)}</select></label>
          </div>
          <label className="comp-fld"><span>Rule Description <Opt /></span><textarea rows="2" value={desc} disabled={readOnly} placeholder="e.g., Controlled carbohydrate intake for diabetic patients" onChange={e => setDesc(e.target.value)} /></label>

          <h4 className="cw-sec-title">Nutrient thresholds</h4>
          <p className="cw-sec-sub">Define limits independently for single ingredients and full recipes.</p>
          <div className="cw-profile-tabs">
            <button className={`cw-profile-tab ${profile === "ingredient" ? "on" : ""}`} onClick={() => setProfile("ingredient")}>Ingredient level <span className="cw-profile-n">{ingRows.length}</span></button>
            <button className={`cw-profile-tab ${profile === "recipe" ? "on" : ""}`} onClick={() => setProfile("recipe")}>Recipe level <span className="cw-profile-n">{recRows.length}</span></button>
          </div>

          {rows.map((r, i) => (
            <div className="cw-rule-row" key={i}>
              <div className="cw-rule-row-head"><Icon name="grip-vertical" size={14} /><span>{String(i + 1).padStart(2, "0")} · {profile} threshold</span>{!readOnly && <button className="cw-rule-del" onClick={() => delRow(i)}><Icon name="trash-2" size={14} /></button>}</div>
              <div className="cw-fld-row">
                <label className="comp-fld"><span>Nutrient</span>
                  <CompMultiSelect options={COMP_NUTRIENTS} value={r.nutrient ? [r.nutrient] : []} onChange={(v) => updRow(i, "nutrient", v[0] || "")} placeholder="e.g., Total Carbohydrate" searchPlaceholder="Search nutrition" disabled={readOnly} single />
                </label>
                <label className="comp-fld"><span>Severity if breached</span>
                  <select value={r.severity} disabled={readOnly} onChange={e => updRow(i, "severity", e.target.value)}>{COMP_SEVERITY.map(s => <option key={s}>{s}</option>)}</select>
                </label>
              </div>
              <div className="cw-fld-row">
                <label className="comp-fld"><span>Min <Opt /></span><input value={r.min} disabled={readOnly} placeholder="e.g., 10g" onChange={e => updRow(i, "min", e.target.value)} /></label>
                <label className="comp-fld"><span>Max <Opt /></span><input value={r.max} disabled={readOnly} placeholder="e.g., 25g" onChange={e => updRow(i, "max", e.target.value)} /></label>
              </div>
            </div>
          ))}
          {!readOnly && <button className="cw-add-row" onClick={addRow}><Icon name="plus" size={14} /> Add another {profile} threshold</button>}
          <CwActivate on={active} set={setActive} disabled={readOnly} />
        </div>
      )}
      {step === 2 && <CwMapTags value={tags} onChange={setTags} disabled={readOnly} />}
      {step === 3 && (
        <div className="cw-pane">
          <CwGuide>Here's the finished rule. The live panel shows exactly which recipes it would flag today, review, then save.</CwGuide>
          <h3 className="cw-step-title">Review &amp; Save</h3>
          <div className="cw-review">
            <div className="cw-review-title">Rule name: {name || "Untitled rule"}</div>
            <CwKV k="Source" v={source.replace(" (default)", "")} />
            <CwKV k="Rule Description" v={desc || "—"} />
            {allLimits.filter(r => r.nutrient).map((r, i) => (
              <div className="cw-review-grid" key={i}>
                <div><span className="cw-rk">{r.scope === "recipe" ? "Recipe" : "Ingredient"} nutrient</span><span className="cw-rv">{cwNutrShort(r.nutrient)}</span></div>
                <div><span className="cw-rk">Severity</span><span className="cw-rv">{r.severity}</span></div>
                <div><span className="cw-rk">Min</span><span className="cw-rv">{r.min || "—"}</span></div>
                <div><span className="cw-rk">Max</span><span className="cw-rv">{r.max || "—"}</span></div>
              </div>
            ))}
            <div className="cw-review-tags">{tags.map(t => <span className="cw-rtag" key={t}><span className="cw-rtag-ph" style={{ backgroundImage: `url("${(typeof compTagPhoto === "function" ? compTagPhoto(t) : COMP_TAG_PHOTOS[t])}")` }} /><Icon name="circle-check" size={13} /> {t}</span>)}</div>
          </div>
        </div>
      )}
    </CwShell>
  );
}
function blankLimit(scope) { return { nutrient: "", severity: "Minimal", min: "", max: "", scope }; }

/* ═══════════ Ingredient rule wizard ═══════════ */
function CwIngredient({ mode, row, readOnly, onSave, onCancel, step, setStep }) {
  const steps = ["Method", "Limits & activate", "Map tags", "Review"];
  const [name, setName] = useCwState(row ? row.name : "");
  const [source, setSource] = useCwState(row ? (/custom/i.test(row.source) ? "Custom (default)" : "Regulatory") : "Custom (default)");
  const [desc, setDesc] = useCwState(row ? row.desc : "");
  const [ings, setIngs] = useCwState(["Milk", "Cheese"]);
  const [allergen, setAllergen] = useCwState("Diary/Milk");
  const [severity, setSeverity] = useCwState(row ? row.severity : "Minimal");
  const [alts, setAlts] = useCwState([{ ing: "Almond milk", sev: "Minimal", nutrient: "Protein (g per 100g)", min: "10g", max: "25g", note: "Almond milk works as an alternative but has much-reduced protein below 30g" }]);
  const [tags, setTags] = useCwState(row ? (row.tags || []).filter((t) => COMP_TAG_PHOTOS[t]) : ["Dairy free"]);
  const [active, setActive] = useCwState(row ? row.status === "active" : true);

  const updAlt = (i, k, v) => setAlts(a => a.map((x, j) => j === i ? { ...x, [k]: v } : x));
  const addAlt = () => setAlts(a => [...a, { ing: "", sev: "Minimal", nutrient: "", min: "", max: "", note: "" }]);
  const delAlt = (i) => setAlts(a => a.filter((_, j) => j !== i));

  const draft = { type: "Ingredient", name, desc, source, active, severity, ingredients: ings, alts, note: allergen ? `Linked to ${allergen} allergen` : "", tags };

  const doSave = async () => {
    const id = row ? row.id : "cr-" + Date.now();
    const ruleRow = {
      id, __remote: !!(row && row.__remote),
      name: name || "Untitled rule", desc: desc || "Ingredient control rule",
      type: "Ingredient", status: active ? "active" : "inactive",
      severity, source: source.replace(" (default)", ""), tags, updated: todayStr(),
    };
    await compSaveWizardRule("rules", ruleRow, {
      description: ruleRow.desc,
      conditions: { ingredients: ings, alternatives: alts, allergen_note: allergen || "" },
      thresholds: { severity: severity },
    });
    onSave();
  };

  return (
    <CwShell title={mode === "edit" ? "Edit Ingredient Swap & Alternative Rule" : mode === "view" ? "Ingredient Swap & Alternative Rule" : "Add Ingredient Swap & Alternative Rule"}
      subtitle="Control ingredients, link allergens and offer alternatives" steps={steps} step={step} setStep={setStep}
      onCancel={onCancel} onBack={step > 0 ? () => setStep(step - 1) : null}
      onNext={step < steps.length - 1 ? () => setStep(step + 1) : null}
      onSave={step === steps.length - 1 && !readOnly ? doSave : null} readOnly={readOnly}
      preview={<CwLivePreview draft={draft} />} loraa={<CwLoraa draft={draft} setStep={setStep} />}>
      {step === 0 && <CwMethod noun="rule" onScratch={() => setStep(1)} readOnly={readOnly} />}
      {step === 1 && (
        <div className="cw-pane">
          <CwGuide>Pick the ingredients to watch and the severity to flag at. Add alternatives with their nutrient targets so cooks know what to swap to.</CwGuide>
          <div className="cw-fld-row">
            <label className="comp-fld"><span>Rule Name</span><input value={name} disabled={readOnly} placeholder="Dairy ingredient control" onChange={e => setName(e.target.value)} /></label>
            <label className="comp-fld"><span>Source</span><select value={source} disabled={readOnly} onChange={e => setSource(e.target.value)}>{COMP_SOURCES.map(s => <option key={s}>{s}</option>)}</select></label>
          </div>
          <label className="comp-fld"><span>Rule Description <Opt /></span><textarea rows="2" value={desc} disabled={readOnly} placeholder="Controlled dairy intake for lactose intolerant patients" onChange={e => setDesc(e.target.value)} /></label>
          <label className="comp-fld"><span>Watched ingredients</span><CompMultiSelect options={COMP_INGREDIENTS} value={ings} onChange={setIngs} placeholder="Select ingredient" searchPlaceholder="Search ingredient" disabled={readOnly} /></label>
          <div className="cw-fld-row">
            <label className="comp-fld"><span>Link to allergen <Opt /></span><input value={allergen} disabled={readOnly} onChange={e => setAllergen(e.target.value)} /></label>
            <label className="comp-fld"><span>Severity Level</span><select value={severity} disabled={readOnly} onChange={e => setSeverity(e.target.value)}>{COMP_SEVERITY.map(s => <option key={s}>{s}</option>)}</select></label>
          </div>

          <h4 className="cw-sec-title">Alternatives <Opt /></h4>
          <p className="cw-sec-sub">Each alternative can carry its own nutrient measurement and severity.</p>
          {alts.map((a, i) => (
            <div className="cw-rule-row" key={i}>
              <div className="cw-rule-row-head"><Icon name="grip-vertical" size={14} /><span>Alternative {String(i + 1).padStart(2, "0")}</span>{!readOnly && <button className="cw-rule-del" onClick={() => delAlt(i)}><Icon name="trash-2" size={14} /></button>}</div>
              <div className="cw-fld-row">
                <label className="comp-fld"><span>Ingredient</span><CompMultiSelect options={COMP_INGREDIENTS} value={a.ing ? [a.ing] : []} searchPlaceholder="Search ingredient" onChange={v => updAlt(i, "ing", v[0] || "")} placeholder="Select ingredient" disabled={readOnly} single /></label>
                <label className="comp-fld"><span>Severity Level</span><select value={a.sev} disabled={readOnly} onChange={e => updAlt(i, "sev", e.target.value)}>{COMP_SEVERITY.map(s => <option key={s}>{s}</option>)}</select></label>
              </div>
              <label className="comp-fld"><span>Nutrient measurement <Opt /></span><CompMultiSelect options={COMP_NUTRIENTS} value={a.nutrient ? [a.nutrient] : []} onChange={v => updAlt(i, "nutrient", v[0] || "")} placeholder="Protein (g) per 100g" searchPlaceholder="Search nutrition" disabled={readOnly} single /></label>
              <div className="cw-fld-row">
                <label className="comp-fld"><span>Min <Opt /></span><input value={a.min} disabled={readOnly} placeholder="10g" onChange={e => updAlt(i, "min", e.target.value)} /></label>
                <label className="comp-fld"><span>Max <Opt /></span><input value={a.max} disabled={readOnly} placeholder="25g" onChange={e => updAlt(i, "max", e.target.value)} /></label>
              </div>
              <label className="comp-fld"><span>Note <Opt /></span><textarea rows="2" value={a.note} disabled={readOnly} onChange={e => updAlt(i, "note", e.target.value)} /></label>
            </div>
          ))}
          {!readOnly && <button className="cw-add-row" onClick={addAlt}><Icon name="plus" size={14} /> Add alternative ingredient</button>}
          <CwActivate on={active} set={setActive} disabled={readOnly} />
        </div>
      )}
      {step === 2 && <CwMapTags value={tags} onChange={setTags} disabled={readOnly} />}
      {step === 3 && (
        <div className="cw-pane">
          <CwGuide>Review the control and its alternatives. The live panel shows which recipes contain the watched ingredients today.</CwGuide>
          <h3 className="cw-step-title">Review &amp; Save</h3>
          <div className="cw-review">
            <div className="cw-review-title">Rule name: {name || "Untitled rule"}</div>
            <CwKV k="Source" v={source.replace(" (default)", "")} />
            <CwKV k="Rule Description" v={desc || "—"} />
            <div className="cw-review-grid"><div><span className="cw-rk">Ingredients</span><span className="cw-rv">{ings.join(", ") || "—"}</span></div><div><span className="cw-rk">Link to allergen</span><span className="cw-rv">{allergen}</span></div><div><span className="cw-rk">Severity</span><span className="cw-rv">{severity}</span></div></div>
            {alts.map((a, i) => (
              <div className="cw-review-grid" key={i}>
                <div><span className="cw-rk">Alternative {String(i + 1).padStart(2, "0")}</span><span className="cw-rv">{a.ing || "—"}</span></div>
                <div><span className="cw-rk">Nutrition</span><span className="cw-rv">{cwNutrShort(a.nutrient) || "—"}</span></div>
                <div><span className="cw-rk">Min</span><span className="cw-rv">{a.min || "—"}</span></div>
                <div><span className="cw-rk">Max</span><span className="cw-rv">{a.max || "—"}</span></div>
              </div>
            ))}
            <div className="cw-review-tags">{tags.map(t => <span className="cw-rtag" key={t}><span className="cw-rtag-ph" style={{ backgroundImage: `url("${(typeof compTagPhoto === "function" ? compTagPhoto(t) : COMP_TAG_PHOTOS[t])}")` }} /><Icon name="circle-check" size={13} /> {t}</span>)}</div>
          </div>
        </div>
      )}
    </CwShell>
  );
}

/* ═══════════ Allergen rule wizard ═══════════ */
function cwIngredientOption(ingredient) {
  if (!ingredient) return null;
  const id = ingredient.value != null ? ingredient.value : ingredient.id;
  const name = ingredient.label != null ? ingredient.label : ingredient.name;
  if (id == null || !name) return null;
  return { value: String(id), label: String(name) };
}
function cwMergeIngredientOptions(existing, incoming) {
  const byId = {};
  (existing || []).concat(incoming || []).forEach((ingredient) => {
    const option = cwIngredientOption(ingredient);
    if (option) byId[option.value] = option;
  });
  return Object.keys(byId).map((id) => byId[id]).sort((a, b) => a.label.localeCompare(b.label));
}
function cwInitialAllergenIngredients(row) {
  const linked = Array.isArray(row && row.linked_ingredients) ? row.linked_ingredients : [];
  const legacy = !linked.length && Array.isArray(row && row.ingredients)
    ? row.ingredients.map((name) => ({ value: "legacy:" + name, label: name }))
    : [];
  const options = cwMergeIngredientOptions([], linked.concat(legacy));
  const ids = Array.isArray(row && row.ingredient_ids)
    ? row.ingredient_ids.map(String)
    : options.map((ingredient) => ingredient.value);
  return { ids: Array.from(new Set(ids)), options };
}
function CwAllergen({ mode, row, readOnly, onSave, onCancel, step, setStep }) {
  const steps = ["Method", "Link & severity", "Review"];
  const [name, setName] = useCwState(row ? row.allergen : "");
  const [source, setSource] = useCwState(row ? (/custom/i.test(row.source) ? "Custom (default)" : "Regulatory") : "Custom (default)");
  const [severity, setSeverity] = useCwState(row ? row.severity : "Critical");
  const initialIngredients = useCwMemo(() => cwInitialAllergenIngredients(row), [row]);
  const [ingredientIds, setIngredientIds] = useCwState(initialIngredients.ids);
  const [ingredientOptions, setIngredientOptions] = useCwState(initialIngredients.options);
  const [ingredientSearch, setIngredientSearch] = useCwState("");
  const [ingredientLoading, setIngredientLoading] = useCwState(false);
  const [ingredientError, setIngredientError] = useCwState("");
  const [note, setNote] = useCwState(row ? row.note : "");
  const [active, setActive] = useCwState(row ? row.status === "active" : true);

  useCwEffect(() => {
    let cancelled = false;
    const requestId = window.setTimeout(async () => {
      const api = window.NutriData && window.NutriData.ingredients;
      if (!api || !api.active) {
        if (!cancelled) setIngredientError("Connect to your organization to load active ingredients.");
        return;
      }
      if (!cancelled) { setIngredientLoading(true); setIngredientError(""); }
      try {
        const response = await api.active({ search: ingredientSearch.trim(), page_size: 100 });
        if (!response) throw new Error("Active ingredients are unavailable.");
        const rows = Array.isArray(response) ? response : (response.results || []);
        const fetched = rows.map(cwIngredientOption).filter(Boolean);
        if (!cancelled) setIngredientOptions((current) => cwMergeIngredientOptions(current, fetched));
      } catch (error) {
        if (!cancelled) setIngredientError((error && error.message) || "Active ingredients could not be loaded.");
      } finally {
        if (!cancelled) setIngredientLoading(false);
      }
    }, ingredientSearch ? 250 : 0);
    return () => { cancelled = true; window.clearTimeout(requestId); };
  }, [ingredientSearch]);

  const ings = useCwMemo(() => ingredientIds.map((id) => {
    const option = ingredientOptions.find((ingredient) => ingredient.value === id);
    return option ? option.label : id;
  }), [ingredientIds, ingredientOptions]);

  const draft = { type: "Allergen", name, source, active, severity, ingredients: ings, note };

  const doSave = async () => {
    const id = row ? row.id : "al-" + Date.now();
    const ruleRow = {
      id, __remote: !!(row && row.__remote),
      name: name || "Untitled allergen", allergen: name || "Untitled allergen",
      allergen_key: name || (row && row.allergen_key) || "Untitled allergen",
      type: "Allergen", source: source.replace(" (default)", ""), severity,
      ingredient_ids: ingredientIds, linked_ingredients: ingredientOptions.filter((ingredient) => ingredientIds.includes(ingredient.value)),
      ingredients: ings, note, status: active ? "active" : "inactive", tags: [],
    };
    await compSaveWizardRule("allergens", ruleRow);
    onSave();
  };

  return (
    <CwShell title={mode === "edit" ? "Edit Allergen Rule" : mode === "view" ? "Allergen Rule" : "Add Allergen Rule"}
      subtitle="Link allergens to ingredients and set severity" steps={steps} step={step} setStep={setStep}
      onCancel={onCancel} onBack={step > 0 ? () => setStep(step - 1) : null}
      onNext={step < steps.length - 1 ? () => setStep(step + 1) : null}
      onSave={step === steps.length - 1 && !readOnly ? doSave : null} readOnly={readOnly}
      preview={<CwLivePreview draft={draft} />} loraa={<CwLoraa draft={draft} setStep={setStep} />}>
      {step === 0 && <CwMethod noun="allergen rule" onScratch={() => setStep(1)} readOnly={readOnly} />}
      {step === 1 && (
        <div className="cw-pane">
          <CwGuide>Name the allergen and link every ingredient that carries it. Any recipe with one of these is flagged at the severity you choose.</CwGuide>
          <label className="comp-fld"><span>Allergen Name</span><input value={name} disabled={readOnly} placeholder="e.g., Egg, Fish" onChange={(e) => setName(e.target.value)} /></label>
          <div className="cw-fld-row">
            <label className="comp-fld"><span>Source</span><select value={source} disabled={readOnly} onChange={(e) => setSource(e.target.value)}>{COMP_SOURCES.map((s) => <option key={s}>{s}</option>)}</select></label>
            <label className="comp-fld"><span>Severity</span><select value={severity} disabled={readOnly} onChange={(e) => setSeverity(e.target.value)}>{COMP_SEVERITY.map((s) => <option key={s}>{s}</option>)}</select></label>
          </div>
          <label className="comp-fld"><span>Linked ingredients</span><CompMultiSelect options={ingredientOptions} value={ingredientIds} onChange={setIngredientIds} onSearchChange={setIngredientSearch} placeholder="Select active organization ingredients" searchPlaceholder="Search active ingredients" disabled={readOnly} /></label>
          {ingredientLoading && <div className="cw-note"><Icon name="loader" size={14} /> Loading active organization ingredients…</div>}
          {ingredientError && <div className="cw-note" style={{ background: "#FFF1F3", color: "#B42318" }}><Icon name="triangle-alert" size={14} /> {ingredientError}</div>}
          <label className="comp-fld"><span>Cross-Contact Note <Opt /></span><textarea rows="3" value={note} disabled={readOnly} placeholder="e.g., Baked goods may contain this allergen" onChange={(e) => setNote(e.target.value)} /></label>
          <CwActivate on={active} set={setActive} disabled={readOnly} />
        </div>
      )}
      {step === 2 && (
        <div className="cw-pane">
          <CwGuide>Confirm the allergen link. The live panel lists every recipe that currently contains one of these ingredients.</CwGuide>
          <h3 className="cw-step-title">Review &amp; Save</h3>
          <div className="cw-review">
            <div className="cw-review-title">Allergen: {name || "Untitled allergen"}</div>
            <div className="cw-review-grid"><div><span className="cw-rk">Source</span><span className="cw-rv">{source.replace(" (default)", "")}</span></div><div><span className="cw-rk">Severity</span><span className="cw-rv">{severity}</span></div></div>
            <div className="cw-rk" style={{ marginTop: 4 }}>Linked ingredients</div>
            <div className="cw-rev-chips">{ings.length ? ings.map(x => <span className="cw-rev-chip" key={x}>{x}</span>) : <span className="cw-rv">—</span>}</div>
            <CwKV k="Cross-Contact Note" v={note || "—"} />
          </div>
        </div>
      )}
    </CwShell>
  );
}

/* ═══════════ Health Tag rule wizard ═══════════ */
function CwHealthTag({ mode, row, readOnly, onSave, onCancel, step, setStep }) {
  const steps = ["Method", "Health tag", "Recommendations", "Review"];
  const [tag, setTag] = useCwState(row ? row.tag : "Low Sodium");
  const [customTags, setCustomTags] = useCwState([]);
  const [source, setSource] = useCwState(row ? (/custom/i.test(row.source) ? "Custom (default)" : "Regulatory") : "Custom (default)");
  const [desc, setDesc] = useCwState(row ? "Controlled diary intake for lactose intolerant patients" : "");
  const [allergen, setAllergen] = useCwState(row ? row.tag : "Low Sodium");
  const [severity, setSeverity] = useCwState(row ? row.severity : "Minimal");
  const [conds, setConds] = useCwState(row ? row.conditions.slice(0, 3) : ["Hypertension", "Heart health", "CKD"]);
  const [low, setLow] = useCwState(["Vitamin C", "Manganese", "Potassium"]);
  const [mod, setMod] = useCwState(["Zinc", "Fiber", "Calcium"]);
  const [high, setHigh] = useCwState(["Sodium", "Vitamin D", "Magnesium"]);
  const [note, setNote] = useCwState("This meal is designed to support reduced sodium intake, but it is not medical advice. Please follow the sodium limit recommended by your doctor or dietitian.");
  const [active, setActive] = useCwState(row ? row.status === "active" : true);

  const draft = { type: "Health Tag", name: tag, tag, desc, source, active, severity, conditions: conds, low, mod, high, note };

  const doSave = async () => {
    const id = row ? row.id : "ht-" + Date.now();
    const nutrients = [...low, ...mod, ...high].slice(0, 6);
    const ruleRow = {
      id, __remote: !!(row && row.__remote),
      name: tag, tag, desc: desc || "Organization health-tag rule",
      type: "Health Tag", conditions: conds, status: active ? "active" : "inactive",
      severity, nutrients, source: source.replace(" (default)", ""), tags: [tag], updated: todayStr(),
    };
    await compSaveWizardRule("healthtags", ruleRow, {
      ruleType: "health_tag",
      description: ruleRow.desc,
      conditions: { health_conditions: conds, nutrients: nutrients, note: note || "" },
      thresholds: { severity: severity },
    });
    onSave();
  };
  const NUTR = COMP_NUTRIENTS.map(n => n.replace(/\s*\(.*\)/, ""));

  return (
    <CwShell title={mode === "edit" ? "Edit Health Tag Rule" : mode === "view" ? "Health Tag Rule" : "Add Health Tag Rule"}
      subtitle="Map a tag to conditions and nutrient recommendations" steps={steps} step={step} setStep={setStep}
      onCancel={onCancel} onBack={step > 0 ? () => setStep(step - 1) : null}
      onNext={step < steps.length - 1 ? () => setStep(step + 1) : null}
      onSave={step === steps.length - 1 && !readOnly ? doSave : null} readOnly={readOnly} saveLabel={mode === "edit" ? "Save changes" : "Save"}
      preview={<CwLivePreview draft={draft} />} loraa={<CwLoraa draft={draft} setStep={setStep} />}>
      {step === 0 && <CwMethod noun="rule" onScratch={() => setStep(1)} readOnly={readOnly} />}
      {step === 1 && (
        <div className="cw-pane">
          <CwGuide>Choose the tag and the conditions it serves. These conditions decide which patients are matched to meals carrying this tag.</CwGuide>
          <label className="comp-fld"><span>Select health tag</span><CompMultiSelect options={COMP_HEALTH_TAGS.concat(customTags)} value={[tag]} onChange={v => setTag(v[0] || tag)} placeholder="Low Sodium" searchPlaceholder="Search or create a health tag" disabled={readOnly} single allowCreate onCreate={(t) => { setCustomTags(c => c.includes(t) ? c : [...c, t]); if (COMP_HEALTH_TAGS.indexOf(t) < 0) COMP_HEALTH_TAGS.push(t); }} /></label>
          <label className="comp-fld"><span>Source</span><select value={source} disabled={readOnly} onChange={e => setSource(e.target.value)}>{COMP_SOURCES.map(s => <option key={s}>{s}</option>)}</select></label>
          <label className="comp-fld"><span>Rule Description <Opt /></span><textarea rows="3" value={desc} disabled={readOnly} placeholder="Controlled diary intake for lactose intolerant patients" onChange={e => setDesc(e.target.value)} /></label>
          <div className="cw-fld-row">
            <label className="comp-fld"><span>Link to allergen <Opt /></span><input value={allergen} disabled={readOnly} onChange={e => setAllergen(e.target.value)} /></label>
            <label className="comp-fld"><span>Severity Level</span><select value={severity} disabled={readOnly} onChange={e => setSeverity(e.target.value)}>{COMP_SEVERITY.map(s => <option key={s}>{s}</option>)}</select></label>
          </div>
          <label className="comp-fld"><span>Linked Health conditions</span><CompMultiSelect options={typeof compConditionNames === "function" ? compConditionNames() : COMP_CONDITIONS} value={conds} onChange={setConds} placeholder="Select health condition" searchPlaceholder="Search condition" disabled={readOnly} /></label>
          <CwActivate on={active} set={setActive} disabled={readOnly} />
        </div>
      )}
      {step === 2 && (
        <div className="cw-pane">
          <CwGuide>Sort nutrients into keep-low, moderate and encourage. The recommendation map on the right mirrors how meals get scored.</CwGuide>
          <label className="comp-fld"><span>Keep low <em className="cw-tone-r">(restricted)</em></span><CompMultiSelect options={NUTR} value={low} onChange={setLow} placeholder="Select nutrient" searchPlaceholder="Search nutrition" disabled={readOnly} /></label>
          <label className="comp-fld"><span>Moderate <em className="cw-tone-a">(in moderation)</em></span><CompMultiSelect options={NUTR} value={mod} onChange={setMod} placeholder="Select nutrient" searchPlaceholder="Search nutrition" disabled={readOnly} /></label>
          <label className="comp-fld"><span>Encourage <em className="cw-tone-g">(in large quantity)</em></span><CompMultiSelect options={NUTR} value={high} onChange={setHigh} placeholder="Select nutrient" searchPlaceholder="Search nutrition" disabled={readOnly} /></label>
          <label className="comp-fld"><span>Safety Note <Opt /></span><textarea rows="4" value={note} disabled={readOnly} onChange={e => setNote(e.target.value)} /></label>
          <CwActivate on={active} set={setActive} disabled={readOnly} />
        </div>
      )}
      {step === 3 && (
        <div className="cw-pane">
          <CwGuide>The tag is ready. Everything on the right is what patients and cooks will see when this tag is applied.</CwGuide>
          <h3 className="cw-step-title">Review &amp; Save</h3>
          <div className="cw-review">
            <CwKV k="Health Tag" v={tag} />
            <CwKV k="Source" v={source.replace(" (default)", "")} />
            <CwKV k="Rule Description" v={desc || "—"} />
            <div className="cw-review-grid"><div><span className="cw-rk">Severity Level</span><span className="cw-rv">{severity}</span></div><div><span className="cw-rk">Status</span><span className="cw-rv">{active ? "Active" : "Inactive"}</span></div></div>
            <div className="cw-rk" style={{ marginTop: 4 }}>Linked Health Conditions</div>
            <div className="cw-rev-chips">{conds.map(c => <span className="cw-rev-chip" key={c}>{c}</span>)}</div>
            <div className="cw-rk" style={{ marginTop: 8 }}>Safety note</div>
            <div className="cw-rv">{note}</div>
          </div>
        </div>
      )}
    </CwShell>
  );
}

/* ═══════════ Wizard shell, large centered two-pane popup ═══════════ */
function CwShell({ title, subtitle, steps, step, setStep, children, onCancel, onBack, onNext, onSave, readOnly, saveLabel, preview, loraa }) {
  useCwEffect(() => {
    const onKey = (e) => { if (e.key === "Escape") onCancel(); };
    document.addEventListener("keydown", onKey);
    return () => document.removeEventListener("keydown", onKey);
  }, []);
  return (
    <div className="cw-overlay" onMouseDown={(e) => { if (e.target === e.currentTarget) onCancel(); }}>
      <div className="cw-pro" role="dialog" aria-label={title}>
        <div className="cw-pro-head">
          <div className="cw-pro-head-t"><h2>{title}</h2><p>{subtitle}</p></div>
          <button className="comp-modal-x" onClick={onCancel}><Icon name="x" size={18} /></button>
        </div>
        <div className="cw-pro-main">
          <div className="cw-pro-left">
            <CwSteps steps={steps} active={step} onJump={setStep} />
            <div className="cw-pro-body">{children}</div>
            <div className="cw-pro-foot">
              {onBack ? <button className="btn ghost" onClick={onBack}><Icon name="arrow-left" size={15} /> Back</button> : <span />}
              <div className="cw-foot-r">
                <button className="btn secondary" onClick={onCancel}>{readOnly ? "Close" : "Cancel"}</button>
                {onNext && <button className="btn primary" onClick={onNext}>Next</button>}
                {onSave && <button className="btn primary" onClick={onSave}>{saveLabel || "Save Rule"}</button>}
              </div>
            </div>
          </div>
          <div className="cw-pro-right">{loraa}{preview}</div>
        </div>
      </div>
    </div>
  );
}
function CwKV({ k, v }) { return <div className="cw-kv"><span className="cw-rk">{k}</span><span className="cw-rv">{v}</span></div>; }
function todayStr() { const d = new Date(); return `${String(d.getDate()).padStart(2, "0")}/${String(d.getMonth() + 1).padStart(2, "0")}/${d.getFullYear()}`; }

/* Host, opened via openCompliance({type,mode,row,onSaved}) */
function ComplianceWizardHost({ state, onClose }) {
  const { role } = useApp();
  const [step, setStep] = useCwState(0);
  useCwEffect(() => { setStep(0); }, [state]);
  if (!state) return null;
  const readOnly = state.mode === "view" || !canEditCompliance(role);
  const done = () => { state.onSaved && state.onSaved(); onClose(); };
  const props = { mode: state.mode, row: state.row, readOnly, onSave: done, onCancel: onClose, step, setStep };
  if (state.type === "ingredient") return <CwIngredient {...props} />;
  if (state.type === "healthtag") return <CwHealthTag {...props} />;
  if (state.type === "allergen") return <CwAllergen {...props} />;
  return <CwNutrition {...props} />;
}

Object.assign(window, { ComplianceWizardHost });
