/* NutriDMS, Workflow-Aware Review Actions (engine + shared UI)
   Reads the org's saved Publishing Workflow (Settings) and turns it into:
   · the current/next step + assignee for a content item
   · the dynamic primary action (Complete Review / Approve / Approve & Publish / View Published)
   · a dynamic Review Timeline generated from the configured steps
   · a responsive action bar (desktop row · tablet Actions menu · mobile sticky bar)
   · review-completion modals (review step + final approval)
   Used by recipe-detail and ingredient-detail. No hardcoded review buttons. */

const { useState: weUseState, useEffect: weUseEffect } = React;

/* Clickable org-compliance badge → opens a popover with the exact org checks
   and a colour legend (green pass · amber warning · red blocking issue). */
function WeOrgBadge({ badge, level }) {
  const [open, setOpen] = weUseState(false);
  const [expanded, setExpanded] = weUseState(null);   // index of the finding being investigated
  const [explain, setExplain] = weUseState(null);     // index showing evidence
  weUseEffect(() => {
    if (!open) return;
    const close = () => { setOpen(false); setExpanded(null); setExplain(null); };
    window.addEventListener("click", close);
    return () => window.removeEventListener("click", close);
  }, [open]);
  const findings = badge.findings || [];
  const sevTone = (s) => (["Critical", "Severe"].includes(s) ? "fail" : (s === "Warning" || s === "Moderate") ? "warn" : "info");

  // ── Health score: passes vs warnings vs blocking across the active rule set ──
  const blocking = findings.filter((f) => sevTone(f.severity) === "fail").length;
  const warnings = findings.filter((f) => sevTone(f.severity) === "warn").length;
  const totalRules = Math.max(findings.length + 5, 7);      // assume a baseline rule set
  const passed = totalRules - blocking - warnings;
  const score = Math.round((passed + warnings * 0.5) / totalRules * 100);
  const scoreState = blocking ? "fail" : warnings ? "warn" : "pass";
  const readiness = blocking ? "Blocked from publishing" : warnings ? "Passes with warnings" : "Ready for publishing";

  // Evidence sources every rule can cite (the "Explain" layer).
  const EVIDENCE = [
    ["building-2", "Organization policy", (f) => (f.ref || "ORG-RULE") + " — internal governance rule configured in Settings › Compliance."],
  ];

  return (
    <span className="wf-orgbadge-wrap" onClick={(e) => e.stopPropagation()}>
      <button className={`wf-orgbadge lg ${badge.state}`} onClick={() => setOpen((o) => !o)}
        title="Click to open the compliance investigation panel" aria-expanded={open}>
        <span className="wf-orgbadge-ic"><Icon name={badge.icon} size={15} stroke={2.7} /></span>
        <span className="wf-orgbadge-t">{badge.label}</span>
        {level && <span className="wf-orgbadge-lvl">{level}</span>}
        <Icon name={open ? "chevron-up" : "chevron-down"} size={13} stroke={2.6} />
      </button>
      {open && (
        <div className="wf-orgpop wide" onClick={(e) => e.stopPropagation()}>
          <div className="wf-orgpop-h">
            <div>
              <strong>Compliance investigation</strong>
              <span>Validated against your org's active rules{level ? " · " + level : ""}</span>
            </div>
            <button className="wf-orgpop-x" onClick={() => setOpen(false)}><Icon name="x" size={15} /></button>
          </div>

          {/* Overall health score */}
          <div className={"wf-orgscore " + scoreState}>
            <div className="wf-orgscore-num">{score}<em>%</em></div>
            <div className="wf-orgscore-mid">
              <div className="wf-orgscore-bar"><i style={{ width: score + "%" }} /></div>
              <div className="wf-orgscore-legend">
                <span className="pass">{passed} passed</span>
                <span className="warn">{warnings} warning</span>
                <span className="fail">{blocking} blocking</span>
              </div>
            </div>
            <div className={"wf-orgscore-ready " + scoreState}>
              <Icon name={blocking ? "shield-alert" : warnings ? "shield" : "shield-check"} size={13} stroke={2.4} />
              {readiness}
            </div>
          </div>

          <div className="wf-orgpop-list">
            {findings.length === 0 ? (
              <div className="wf-orgpop-ok"><Icon name="shield-check" size={18} /> All active org rules pass. Nothing to fix.</div>
            ) : findings.map((f, i) => {
              const tone = sevTone(f.severity);
              const isOpen = expanded === i;
              const cur = f.current != null ? f.current : f.value;
              const allowed = f.allowed != null ? f.allowed : f.limit != null ? f.limit : f.threshold;
              const diff = (typeof cur === "number" && typeof allowed === "number") ? (cur - allowed) : null;
              return (
                <div key={i} className={"wf-inv " + tone + (isOpen ? " open" : "")}>
                  <button className="wf-inv-head" onClick={() => { setExpanded(isOpen ? null : i); setExplain(null); }}>
                    <span className={"wf-inv-sev " + tone}><Icon name={tone === "fail" ? "alert-triangle" : tone === "warn" ? "alert-circle" : "info"} size={13} stroke={2.4} /></span>
                    <span className="wf-inv-name"><b>{f.rule || f.name || f.field || "Rule"}</b><em>{f.tag || f.condition || "Organization policy"}</em></span>
                    <span className={"wf-orgpop-sev " + tone}>{f.severity || "Info"}</span>
                    <Icon name={isOpen ? "chevron-up" : "chevron-down"} size={14} stroke={2.4} />
                  </button>
                  {isOpen && (
                    <div className="wf-inv-body">
                      {(cur != null || allowed != null) && (
                        <div className="wf-inv-stats">
                          <div className="wf-inv-stat"><em>Current</em><b className={tone === "fail" ? "over" : ""}>{cur != null ? cur : "—"}{f.unit || ""}</b></div>
                          <div className="wf-inv-stat"><em>Allowed</em><b>{allowed != null ? allowed : "—"}{f.unit || ""}</b></div>
                          {diff != null && <div className="wf-inv-stat"><em>Difference</em><b className={diff > 0 ? "over" : "ok"}>{diff > 0 ? "+" : ""}{diff}{f.unit || ""}</b></div>}
                          <div className="wf-inv-stat"><em>Severity</em><b className={"sev-" + tone}>{f.severity || "Info"}</b></div>
                        </div>
                      )}
                      <div className="wf-inv-reason">
                        <div className="wf-inv-lbl">Reason</div>
                        <p>{f.detail || f.reason || f.issue || f.note || "This value falls outside the organization's configured threshold for this rule."}</p>
                      </div>
                      {f.fix && (
                        <div className="wf-inv-rec">
                          <div className="wf-inv-lbl"><Icon name="lightbulb" size={12} stroke={2.4} /> Recommendation</div>
                          <p>{f.fix}</p>
                        </div>
                      )}
                      <div className="wf-inv-meta">
                        {f.affected != null && <span><Icon name="users" size={12} /> {f.affected} members affected</span>}
                        {f.ref && <span><Icon name="hash" size={12} /> {f.ref}</span>}
                      </div>
                      <div className="wf-inv-actions">
                        <button className="wf-inv-explain" onClick={() => setExplain(explain === i ? null : i)}>
                          <Icon name="book-open" size={12} stroke={2.4} /> {explain === i ? "Hide evidence" : "Explain — why?"}
                        </button>
                        <button className="wf-inv-open" onClick={() => {
                          var role = window.__role;
                          var canOpen = (typeof canEditCompliance === "function") ? canEditCompliance(role) : (role === "admin" || role === "super-admin");
                          if (canOpen) {
                            try { window.dispatchEvent(new CustomEvent("nutridms-open-rule", { detail: { name: f.rule || f.name || f.field, ref: f.ref, role: role } })); } catch (e) {}
                            setOpen(false);
                          } else {
                            try { window.dispatchEvent(new CustomEvent("nutridms-open-rule", { detail: { name: f.rule || f.name || f.field, ref: f.ref, role: role, viewOnly: true } })); } catch (e) {}
                            setOpen(false);
                          }
                        }}>
                          Open rule <Icon name="arrow-right" size={12} stroke={2.4} />
                        </button>
                      </div>
                      {explain === i && (
                        <div className="wf-inv-evidence">
                          <div className="wf-inv-lbl">Evidence &amp; references</div>
                          {EVIDENCE.map(([ic, src, txt], k) => (
                            <div key={k} className="wf-inv-ev"><span className="wf-inv-ev-ic"><Icon name={ic} size={13} stroke={2.2} /></span><div><b>{src}</b><span>{txt(f)}</span></div></div>
                          ))}
                        </div>
                      )}
                    </div>
                  )}
                </div>
              );
            })}
          </div>
        </div>
      )}
    </span>
  );
}

/* ── Load the active workflow for a content type ── */
function weWorkflow(kind, item) {
  const server = item && item.workflow;
  if (server && Array.isArray(server.stages) && server.stages.length) {
    return {
      name: server.name || "Publishing workflow",
      autoPublish: !!server.auto_publish_after_approval,
      steps: server.stages.map((stage) => {
        const target = stage.assignment || {};
        return {
          id: stage.id || ("server-stage-" + stage.index), name: stage.name,
          type: stage.stage_type || "review", final: !!stage.is_final,
          disabled: false, serverState: stage.state,
          assignee: target.type === "member" ? "user:" + target.member_id
            : target.type === "role" ? "role:" + target.role_id : "eligible",
          assigneeLabel: target.member_name || target.role_label || "Any eligible reviewer",
        };
      }),
      currentStageIndex: server.current_stage && server.current_stage.index,
    };
  }
  const all = window.wfLoad ? window.wfLoad() : null;
  if (!all) return null;
  return all[kind === "ingredient" ? "ingredient" : "recipe"] || null;
}
function weAssigneeLabel(step) {
  if (!step) return "—";
  return step.assigneeLabel || (window.wfAssigneeLabel ? window.wfAssigneeLabel(step.assignee) : "Reviewer");
}

/* ── Compute the full workflow state for an item ── */
function weFlow(item, kind, role) {
  const wf = weWorkflow(kind, item);
  if (!wf) return null;
  const serverActions = item && item.workflow && typeof item.workflow.actions === "object"
    ? item.workflow.actions : null;
  const steps = wf.steps.filter((s) => !s.disabled);
  const autoPub = !!wf.autoPublish;
  const status = item.status;

  let phase, stepIdx;
  if (status === "draft") { phase = "draft"; stepIdx = -1; }
  else if (status === "published") { phase = "published"; stepIdx = steps.length; }
  else if (status === "rejected") { phase = "rejected"; stepIdx = -1; }
  else if (status === "approved") { phase = autoPub ? "published" : "ready"; stepIdx = steps.length; }
  else {
    phase = "review";
    if (typeof wf.currentStageIndex === "number") stepIdx = wf.currentStageIndex;
    else if (typeof item._wfStepIdx === "number") stepIdx = item._wfStepIdx;
    else if (status === "compliance-review") {
      // Resolve by the step's assignee ROLE (config-driven), falling back to name, then position.
      let ci = steps.findIndex((s) => /compliance/i.test(s.assignee || ""));
      if (ci < 0) ci = steps.findIndex((s) => /complian/i.test(s.name));
      stepIdx = ci >= 0 ? ci : Math.min(1, Math.max(0, steps.length - 1));
    } else stepIdx = 0;
    stepIdx = Math.max(0, Math.min(stepIdx, Math.max(0, steps.length - 1)));
  }

  const changes = status === "changes-requested";
  const currentStep = phase === "review" ? steps[stepIdx] : null;
  const nextStep = phase === "review" ? steps[stepIdx + 1] || null : null;
  const isLast = phase === "review" && stepIdx === steps.length - 1;
  const isFinal = phase === "review" && (!!(currentStep && currentStep.final) || isLast);

  const currentAssignee = weAssigneeLabel(currentStep);
  const nextStepLabel = nextStep ? nextStep.name : autoPub ? "Published" : "Ready for Publishing";
  const nextAssignee = nextStep ? weAssigneeLabel(nextStep) : autoPub ? "Auto-published" : "Publisher";

  const canReview = serverActions
    ? !!(serverActions.can_request_changes || serverActions.can_approve)
    : ["reviewer", "compliance", "manager", "admin", "super-admin"].includes(role);
  const canFinalize = serverActions
    ? !!serverActions.can_approve
    : (window.can ? window.can(role, "approve_publish") : ["compliance", "admin", "super-admin"].includes(role));
  const canPublish = serverActions
    ? !!serverActions.can_publish
    : (window.can ? window.can(role, "approve_publish") : ["compliance", "admin", "super-admin"].includes(role));
  const pausedForRetiredRole = !!(serverActions && serverActions.can_repair_workflow);
  const blockedReason = item && item.workflow && item.workflow.blocked_reason;

  let primary = null, actRole = null;
  if (pausedForRetiredRole) {
    primary = { label: "Restart with repaired workflow", kind: "repair-restart", icon: "refresh-cw", tone: "primary" };
    actRole = "repair";
  }
  else if (phase === "published") primary = { label: "View Published Content", kind: "view", icon: "external-link", tone: "secondary" };
  else if (phase === "rejected") primary = null;
  else if (phase === "ready") { primary = { label: "Publish Now", kind: "publish", icon: "globe", tone: "primary" }; actRole = "finalize"; }
  else if (phase === "review") {
    if (isFinal) {
      primary = autoPub
        ? { label: "Approve & Publish", kind: "approve-publish", icon: "globe", tone: "primary" }
        : { label: "Approve", kind: "approve-final", icon: "check", tone: "primary" };
      actRole = "finalize";
    } else if (currentStep.type === "approval") {
      primary = { label: "Approve", kind: "approve-step", icon: "check", tone: "primary" }; actRole = "finalize";
    } else {
      primary = { label: "Complete Review", kind: "complete-review", icon: "check", tone: "primary" }; actRole = "review";
    }
  }
  const canActPrimary = pausedForRetiredRole ? true
    : phase === "ready" ? canPublish
    : actRole === "finalize" ? canFinalize
    : actRole === "review" ? canReview
    : phase === "published";

  return {
    wf, steps, autoPub, phase, stepIdx, changes, currentStep, nextStep, isFinal, isLast,
    currentAssignee, nextStepLabel, nextAssignee, primary, actRole, canActPrimary, canReview, canFinalize,
    pausedForRetiredRole, blockedReason,
    actions: serverActions || {},
  };
}

/* ── Resolve the transition produced by the primary action ── */
function weAdvance(flow) {
  const { phase, stepIdx, steps, isFinal, autoPub } = flow;
  if (phase === "ready") return { status: "published", _wfStepIdx: null, toast: "Published", navAway: true };
  if (isFinal) {
    if (autoPub) return { status: "published", _wfStepIdx: null, toast: "Approved & published — moved to the library", navAway: true };
    return { status: "approved", _wfStepIdx: null, toast: "Approved — moved to the library, ready for publishing", navAway: true };
  }
  const ni = stepIdx + 1;
  const nStep = steps[ni];
  const nStatus = nStep ? ((/compliance/i.test(nStep.assignee || "") || /complian/i.test(nStep.name)) ? "compliance-review" : "pending-review") : "pending-review";
  return { status: nStatus, _wfStepIdx: ni, toast: nStep ? `Moved to ${nStep.name}` : "Review complete" };
}

/* Never synthesize a review transition in the browser. */
async function weServerAction(kind, item, flow) {
  if (!item || !item.__remote) throw new Error("This content is not connected to NutriDMS.");
  const publishing = flow && flow.primary && flow.primary.kind === "publish";
  const restarting = flow && flow.primary && flow.primary.kind === "repair-restart";
  const api = kind === "ingredient" ? window.NutriIngredients : window.NutriRecipes;
  const sync = kind === "ingredient" ? window.IngredientSync : window.RecipeSync;
  if (!api || !(restarting ? api.repairWorkflow : publishing ? api.publish : api.approve)) throw new Error("The workflow service is unavailable.");
  const saved = restarting
    ? await api.repairWorkflow(item.id, { action: "restart" })
    : publishing ? await api.publish(item.id) : await api.approve(item.id);
  if (sync && typeof sync.sync === "function") await sync.sync();
  return saved;
}

/* ── Viewport mode hook ── */
function weViewport() {
  const get = () => {
    const w = typeof window !== "undefined" ? window.innerWidth : 1280;
    return w <= 640 ? "mobile" : w <= 1080 ? "tablet" : "desktop";
  };
  const [mode, setMode] = weUseState(get);
  weUseEffect(() => {
    const on = () => setMode(get());
    window.addEventListener("resize", on);
    return () => window.removeEventListener("resize", on);
  }, []);
  return mode;
}

/* ════════════ Workflow Context Banner ════════════ */
function WorkflowBanner({ flow, item, kind }) {
  if (!flow || flow.phase === "draft") return null;
  let cur, next, assigned, tone = "review";
  if (flow.phase === "published") { cur = "Published"; next = "Live"; assigned = "Public catalog"; tone = "done"; }
  else if (flow.phase === "rejected") { cur = "Rejected"; next = "Returned to contributor"; assigned = "—"; tone = "rej"; }
  else if (flow.phase === "ready") { cur = "Ready for Publishing"; next = "Published"; assigned = flow.nextAssignee; tone = "ready"; }
  else {
    cur = (flow.changes ? "Changes Requested · " : "") + flow.currentStep.name;
    next = flow.nextStepLabel; assigned = flow.currentAssignee;
    tone = flow.changes ? "chg" : "review";
  }

  // ── Org compliance badge: validate the item against the org's active rules ──
  let orgBadge = null;
  if (item && typeof complianceCheck === "function") {
    try {
      const f = complianceCheck(item, kind || "recipe") || [];
      const blocking = f.filter((x) => ["Critical", "Severe"].includes(x.severity)).length;
      const overridden = f.filter((x) => String(x.severity) === "Pass (override)").length;
      const warn = f.length - blocking - overridden;
      if (blocking > 0) orgBadge = { state: "fail", label: `${blocking} org issue${blocking > 1 ? "s" : ""}`, icon: "shield-alert", findings: f };
      else if (warn > 0) orgBadge = { state: "warn", label: `${warn} org check${warn > 1 ? "s" : ""}`, icon: "shield", findings: f };
      else orgBadge = { state: "pass", label: "Org compliant", icon: "shield-check", findings: f };
    } catch (e) { orgBadge = null; }
  }

  // Approval-level stepper: which step of the chain are we currently at
  const steps = flow.steps || [];
  const past = flow.phase === "ready" || flow.phase === "published";
  const stepState = (i) => {
    if (past) return "done";
    if (flow.phase === "rejected") return "upcoming";
    if (flow.phase !== "review") return "upcoming";
    if (i < flow.stepIdx) return "done";
    if (i === flow.stepIdx) return flow.changes ? "changes" : "current";
    return "upcoming";
  };
  const counterLabel = past
    ? "Approved"
    : flow.phase === "rejected"
      ? "Rejected"
      : flow.phase === "review"
        ? `Step ${flow.stepIdx + 1} of ${steps.length}`
        : "";

  // Approval-level segment for the org badge, dynamic with where we are in the chain
  const orgLevel = (flow.phase === "published") ? "Published"
    : (flow.phase === "ready") ? "Final cleared"
    : (flow.phase === "rejected") ? "Returned"
    : (flow.phase === "review" && steps.length) ? `Level ${flow.stepIdx + 1}/${steps.length}`
    : "";

  return (
    <div className={`wf-banner ${tone}`}>
      {flow.blockedReason && <div className="alert warning" style={{ marginBottom: 14 }}><Icon name="pause-circle" size={16} /><div>{flow.blockedReason} Save a repaired workflow, then restart this item to capture the new routing.</div></div>}
      {steps.length > 0 && (
        <div className="wf-banner-stepper">
          <div className="wf-banner-stepper-head">
            <span className="wf-banner-k"><Icon name="list-checks" size={12} stroke={2.4} /> Approval review</span>
            {counterLabel && <span className={`wf-stepcount ${past ? "done" : flow.phase === "rejected" ? "rej" : ""}`}>{counterLabel}</span>}
            {orgBadge && <WeOrgBadge badge={orgBadge} level={orgLevel} />}
          </div>
          <div className="wf-steps">
            {steps.map((s, i) => {
              const st = stepState(i);
              return (
                <React.Fragment key={s.id || i}>
                  {i > 0 && <span className={`wf-step-line ${stepState(i - 1) === "done" ? "done" : ""}`} style={{ "--i": i }} />}
                  <div className={`wf-step ${st}`} style={{ "--i": i }} title={`${s.name} · ${weAssigneeLabel(s)}`}>
                    <span className="wf-step-num">
                      {st === "done" ? <Icon name="check" size={13} stroke={2.6} />
                        : st === "changes" ? <Icon name="message-square" size={12} stroke={2.4} />
                        : st === "current" ? <Icon name="circle-dot" size={13} stroke={2.4} />
                        : i + 1}
                    </span>
                    <span className="wf-step-name">{s.name}</span>
                    {st === "current" && <span className="wf-step-here">You are here</span>}
                    {st === "changes" && <span className="wf-step-here chg">Changes asked</span>}
                  </div>
                </React.Fragment>
              );
            })}
          </div>
        </div>
      )}
      <div className="wf-banner-flow">
        <div className="wf-banner-col">
          <span className="wf-banner-k"><Icon name="git-commit-horizontal" size={12} stroke={2.4} /> Current step</span>
          <span className="wf-banner-v">{cur}</span>
        </div>
        <Icon name="arrow-right" size={16} className="wf-banner-arrow" />
        <div className="wf-banner-col">
          <span className="wf-banner-k"><Icon name="arrow-right-circle" size={12} stroke={2.4} /> Next step</span>
          <span className="wf-banner-v">{next}</span>
        </div>
        <div className="wf-banner-col">
          <span className="wf-banner-k"><Icon name="user-round" size={12} stroke={2.4} /> Assigned to</span>
          <span className="wf-banner-v">{assigned}</span>
        </div>
      </div>
    </div>
  );
}

/* ════════════ Dynamic Review Timeline ════════════ */
function WorkflowTimeline({ flow, status }) {
  if (!flow) return window.Timeline ? <window.Timeline status={status} /> : null;
  const nodes = [{ label: "Draft" }, { label: "Submitted" }, ...flow.steps.map((s) => ({ label: s.name, step: true })), { label: "Published" }];
  let pos;
  if (flow.phase === "draft") pos = 0;
  else if (flow.phase === "rejected") pos = 1;
  else if (flow.phase === "review") pos = 2 + flow.stepIdx;
  else pos = 2 + flow.steps.length;
  const published = flow.phase === "published";
  const rejected = flow.phase === "rejected";

  return (
    <div className="wf-tl">
      {nodes.map((n, i) => {
        const done = i < pos || (i === pos && published);
        const cur = i === pos && !published;
        const isRejNode = rejected && i === 1;
        const isChgNode = flow.changes && cur && n.step;
        const icon = isRejNode ? "x" : done ? "check" : isChgNode ? "message-square" : cur ? "circle-dot" : "circle";
        const color = isRejNode ? "var(--error-600)" : isChgNode ? "#5925DC" : done || cur ? "var(--green-700)" : "var(--gray-400)";
        const bg = isRejNode ? "var(--error-50)" : isChgNode ? "#F4F3FF" : done || cur ? "var(--green-50)" : "var(--gray-50)";
        return (
          <div key={i} className="wf-tl-row">
            <div className="wf-tl-dot" style={{ background: bg, color, borderColor: cur || isRejNode ? color : "transparent" }}>
              <Icon name={icon} size={12} stroke={2.5} />
            </div>
            <div className="wf-tl-label" style={{ fontWeight: cur ? 700 : 500, color: done || cur ? "var(--text-primary)" : "var(--gray-500)" }}>{n.label}</div>
            {cur && isChgNode && <span className="pill violet">changes asked</span>}
            {isRejNode && <span className="pill error">rejected here</span>}
            {cur && !isChgNode && !rejected && <span className="wf-tl-now">Current</span>}
          </div>
        );
      })}
    </div>
  );
}

/* ════════════ Responsive Action Bar ════════════ */
/* actions: [{ id, label, icon, tone, onClick, style }], last item should be the primary */
function WfActionBar({ flow, actions, primary }) {
  const mode = weViewport();
  const [menuOpen, setMenuOpen] = weUseState(false);
  weUseEffect(() => { setMenuOpen(false); }, [mode]);

  const renderBtn = (a, extra) => (
    <button key={a.id} className={`btn ${a.tone || "secondary"}`} style={a.style} onClick={a.onClick} {...extra}>
      {a.icon && <Icon name={a.icon} size={16} />} {a.label}
    </button>
  );

  if (mode === "mobile") {
    // secondaries in a menu (top), primary + request-changes pinned to sticky bottom bar
    const reqChanges = actions.find((a) => a.id === "request-changes");
    const rest = actions.filter((a) => a.id !== "request-changes");
    return (
      <>
        <div className="wf-actbar mobile">
          <button className="btn secondary" onClick={() => setMenuOpen((o) => !o)}>
            <Icon name="more-horizontal" size={16} /> Actions
          </button>
          {menuOpen && (
            <div className="wf-actmenu" onClick={() => setMenuOpen(false)}>
              {rest.map((a) => (
                <button key={a.id} className="wf-actmenu-item" onClick={a.onClick}>
                  {a.icon && <Icon name={a.icon} size={15} />} {a.label}
                </button>
              ))}
            </div>
          )}
        </div>
        <div className="wf-mobilebar">
          {reqChanges && (
            <button className="btn secondary" onClick={reqChanges.onClick}><Icon name={reqChanges.icon} size={16} /> {reqChanges.label}</button>
          )}
          {primary && (
            <button className={`btn ${primary.disabled ? "secondary" : "primary"}`} disabled={primary.disabled} onClick={primary.onClick} style={{ flex: 1 }}>
              <Icon name={primary.icon} size={16} /> {primary.label}
            </button>
          )}
        </div>
      </>
    );
  }

  if (mode === "tablet") {
    // primary stays; everything else collapses into Actions ▾
    return (
      <div className="wf-actbar">
        <div className="wf-actwrap">
          <button className="btn secondary" onClick={() => setMenuOpen((o) => !o)}>
            <Icon name="sliders-horizontal" size={16} /> Actions <Icon name="chevron-down" size={14} />
          </button>
          {menuOpen && (
            <div className="wf-actmenu" onClick={() => setMenuOpen(false)}>
              {actions.map((a) => (
                <button key={a.id} className="wf-actmenu-item" onClick={a.onClick}>
                  {a.icon && <Icon name={a.icon} size={15} />} {a.label}
                </button>
              ))}
            </div>
          )}
        </div>
        {primary && (
          <button className={`btn ${primary.disabled ? "secondary" : "primary"}`} disabled={primary.disabled} onClick={primary.onClick}>
            <Icon name={primary.icon} size={16} /> {primary.label}
          </button>
        )}
      </div>
    );
  }

  // desktop, full inline row
  return (
    <div className="wf-actbar">
      {actions.map((a) => renderBtn(a))}
      {primary && (
        <button className={`btn ${primary.disabled ? "secondary" : "primary"}`} disabled={primary.disabled} onClick={primary.onClick}>
          <Icon name={primary.icon} size={16} /> {primary.label}
        </button>
      )}
    </div>
  );
}

/* ════════════ Review Completion Modal ════════════ */
function ReviewCompleteModal({ open, flow, onClose, onConfirm, onRequestChanges }) {
  const [note, setNote] = weUseState("");
  weUseEffect(() => { if (open) setNote(""); }, [open]);
  if (!flow) return null;
  if (flow.pausedForRetiredRole) {
    return (
      <Modal open={open} onClose={onClose} title="Restart paused workflow" subtitle="This item is paused because its original stage targeted a retired role." footer={<><button className="btn ghost" onClick={onClose}>Cancel</button><button className="btn primary" onClick={() => onConfirm()}><Icon name="refresh-cw" size={14} /> Restart workflow</button></>}>
        <div className="alert warning"><Icon name="pause-circle" size={18} /><div>{flow.blockedReason || "Save a valid workflow first. Restarting captures its current routing without bypassing review."}</div></div>
      </Modal>
    );
  }
  const isFinal = flow.isFinal;
  const title = isFinal ? "Approve content" : `Complete ${flow.currentStep ? flow.currentStep.name : "review"}`;
  const moveTo = flow.nextStepLabel;
  const afterText = flow.autoPub
    ? "This is the final approval step. After approval, this content will be published automatically."
    : isFinal
      ? "This is the final approval step. After approval, this content moves to “Ready for Publishing” for a manual publish."
      : null;

  return (
    <Modal open={open} onClose={onClose} title={title} subtitle={isFinal ? "Final business decision for this content." : "Hand off to the next workflow step."} footer={
      <>
        <button className="btn ghost" style={{ marginRight: "auto", color: "var(--warning-600)" }} onClick={() => { onClose(); onRequestChanges && onRequestChanges(); }}>
          <Icon name="message-square" size={14} /> Request Changes
        </button>
        <button className="btn ghost" onClick={onClose}>Cancel</button>
        <button className="btn primary" onClick={() => onConfirm(note)}>
          <Icon name={flow.primary.icon} size={14} /> {flow.primary.label}
        </button>
      </>
    }>
      {isFinal ? (
        <div className={`alert ${flow.autoPub ? "success" : "info"}`} style={{ marginBottom: 14 }}>
          <Icon name={flow.autoPub ? "globe" : "shield-check"} size={18} />
          <div><strong>{flow.autoPub ? "Auto-publish is on" : "Manual publish step follows"}</strong><div style={{ marginTop: 2 }}>{afterText}</div></div>
        </div>
      ) : (
        <div className="wf-next-block">
          <div className="wf-next-k">Next action, this item will move to</div>
          <div className="wf-next-row">
            <span className="wf-next-step"><Icon name="arrow-right-circle" size={15} stroke={2.2} /> {moveTo}</span>
            <span className="wf-next-assignee"><Icon name="user-round" size={13} stroke={2.2} /> {flow.nextAssignee}</span>
          </div>
        </div>
      )}
      <div className="field" style={{ marginTop: 4 }}>
        <label>{isFinal ? "Approval notes" : "Review notes"} <span className="muted" style={{ fontWeight: 500 }}>· optional</span></label>
        <textarea className="textarea" rows="3" value={note} onChange={(e) => setNote(e.target.value)} placeholder={isFinal ? "Anything to record with this approval…" : "Optional notes for the next reviewer…"} />
      </div>
    </Modal>
  );
}

/* Standalone org-compliance badge — computes findings from an item + kind and
   renders the investigation panel even when no review workflow is active
   (e.g. a draft ingredient). */
function WeComplianceBadge({ item, kind, level }) {
  if (!item || typeof complianceCheck !== "function") return null;
  let badge = null;
  try {
    const f = complianceCheck(item, kind || "recipe") || [];
    const blocking = f.filter((x) => ["Critical", "Severe"].includes(x.severity)).length;
    const overridden = f.filter((x) => String(x.severity) === "Pass (override)").length;
    const warn = f.length - blocking - overridden;
    if (blocking > 0) badge = { state: "fail", label: `${blocking} org issue${blocking > 1 ? "s" : ""}`, icon: "shield-alert", findings: f };
    else if (warn > 0) badge = { state: "warn", label: `${warn} org check${warn > 1 ? "s" : ""}`, icon: "shield", findings: f };
    else badge = { state: "pass", label: "Org compliant", icon: "shield-check", findings: f };
  } catch (e) { return null; }
  return <WeOrgBadge badge={badge} level={level} />;
}

Object.assign(window, { weWorkflow, weFlow, weAdvance, weServerAction, weViewport, WorkflowBanner, WorkflowTimeline, WfActionBar, ReviewCompleteModal, wePatchItem, PublishedLock, WeComplianceBadge });

/* ════════════ Rule Detail Drawer (Open rule → exact org rule) ════════════
   Self-mounting: listens for the "nutridms-open-rule" event, finds the exact
   matched rule in compLoad().rules, and slides in from the right. Editable
   (threshold/severity/save) for admins; view-only with a Contact-Admin notice
   otherwise. Renders into its own portal container so no app-shell edit needed. */
function RuleDrawer() {
  const [state, setState] = weUseState(null); // { rule, kind, canEdit, draft, name, ref } | null
  weUseEffect(() => {
    const onOpen = (e) => {
      const d = (e && e.detail) || {};
      let found = null, kind = "rules";
      try {
        const data = (typeof compLoad === "function") ? compLoad() : {};
        const pools = [["rules", data.rules], ["healthtags", data.healthtags], ["allergens", data.allergens]];
        const needle = (d.name || "").toLowerCase().trim();
        for (const [k, arr] of pools) {
          if (!Array.isArray(arr)) continue;
          const hit = arr.find((r) => (r.id && d.ref && r.id === d.ref) || (r.name && needle && r.name.toLowerCase().trim() === needle) || (needle && r.name && needle.indexOf(r.name.toLowerCase()) >= 0) || (r.allergen && needle && r.allergen.toLowerCase().trim() === needle) || (r.tag && needle && r.tag.toLowerCase().trim() === needle));
          if (hit) { found = hit; kind = k; break; }
        }
      } catch (er) {}
      const role = d.role || window.__role;
      const canEdit = !d.viewOnly && ((typeof canEditCompliance === "function") ? canEditCompliance(role) : (role === "admin" || role === "super-admin"));
      var userName = ""; try { userName = (typeof currentUser === "function" && currentUser(role) && currentUser(role).name) || ""; } catch (e) {}
      var canSave = false; try { canSave = (typeof can === "function") ? can(role, "rules_save") : canEdit; } catch (e) { canSave = canEdit; }
      setState({ rule: found, kind, canEdit, canSave, userName, name: d.name, ref: d.ref, draft: found ? JSON.parse(JSON.stringify(found)) : null });
    };
    window.addEventListener("nutridms-open-rule", onOpen);
    return () => window.removeEventListener("nutridms-open-rule", onOpen);
  }, []);
  if (!state) return null;
  const { rule, kind, canEdit } = state;
  const draft = state.draft || rule || {};
  const close = () => { setState(null); };
  const save = () => {
    const d = state.draft || {};
    const isOverride = String(d.severity) === "Pass (override)";
    if (isOverride) {
      var sigName = state.userName || d.ovSig;
      if (!sigName || !sigName.trim() || !d.ovReason || !d.ovReason.trim()) {
        try { window.__toast && window.__toast("A reason is required to override to Pass."); } catch (e) {}
        return;
      }
      d.ovSig = sigName;
    }
    try {
      if (typeof compUpsert === "function" && state.draft) compUpsert(kind, state.draft);
      if (isOverride) {
        window.__toast && window.__toast("Override applied locally — " + (rule.name || "rule") + " set to Pass for this item.");
      } else {
        window.__toast && window.__toast("Rule updated \u2014 re-validating affected items");
      }
    } catch (e) {}
    close();
  };
  const set = (k, v) => setState((s) => Object.assign({}, s, { draft: Object.assign({}, s.draft || s.rule || {}, { [k]: v }) }));
  const KIND_LABEL = { rules: "Nutrient / policy rule", healthtags: "Health-tag rule", allergens: "Allergen rule" };

  return ReactDOM.createPortal(
    <div className="rule-drawer-scrim" onMouseDown={(e) => { if (e.target === e.currentTarget) close(); }}>
      <div className="rule-drawer">
        <div className="rule-drawer-h">
          <div>
            <span className="rule-drawer-eyebrow"><Icon name="scale" size={12} stroke={2.4} /> {KIND_LABEL[kind] || "Organization rule"}</span>
            <div className="rule-drawer-t">{(rule && rule.name) || state.name || "Rule"}</div>
          </div>
          <button className="rule-drawer-x" onClick={close}><Icon name="x" size={18} /></button>
        </div>

        {!rule ? (
          <div className="rule-drawer-body">
            <div className="alert warning"><Icon name="alert-triangle" size={18} /><div><strong>Rule not found in your active set</strong><div style={{ marginTop: 2 }}>This finding references "{state.name}", which isn't in the current rule list. It may have been renamed or removed. Open the Compliance Center to review.</div></div></div>
            <button className="btn secondary" style={{ marginTop: 12 }} onClick={() => { close(); try { (window.__setPage || (() => {}))("compliance-dashboard"); } catch (e) {} }}>Open Compliance Center</button>
          </div>
        ) : (
          <div className="rule-drawer-body">
            {!canEdit && (
              <div className="rule-drawer-lock"><Icon name="lock" size={15} stroke={2.2} /><div><b>View only</b><span>You can review this rule but not change it. Contact your Admin to adjust the threshold or severity.</span></div></div>
            )}
            <label className="rule-fld"><span>Rule name</span>
              <input className="rule-input" value={draft.name || ""} disabled={true} readOnly />
              <em className="rule-fld-note">Rule name is fixed and cannot be changed here.</em>
            </label>
            {"nutrient" in (rule || {}) && <label className="rule-fld"><span>Nutrient</span>
              <input className="rule-input" value={draft.nutrient || ""} disabled={!canEdit} onChange={(e) => set("nutrient", e.target.value)} />
            </label>}
            <div className="rule-fld-row">
              {("limit" in (rule || {}) || "threshold" in (rule || {}) || "max" in (rule || {})) && (
                <label className="rule-fld"><span>Threshold</span>
                  <input className="rule-input" type="number" value={draft.limit != null ? draft.limit : draft.threshold != null ? draft.threshold : draft.max != null ? draft.max : ""} disabled={!canEdit}
                    onChange={(e) => set("limit" in rule ? "limit" : "threshold" in rule ? "threshold" : "max", e.target.value === "" ? "" : Number(e.target.value))} />
                </label>
              )}
              {"unit" in (rule || {}) && <label className="rule-fld"><span>Unit</span>
                <input className="rule-input" value={draft.unit || ""} disabled={!canEdit} onChange={(e) => set("unit", e.target.value)} />
              </label>}
            </div>
            <label className="rule-fld"><span>Severity</span>
              <select className="rule-input" value={draft.severity && draft.severity !== "Pass (override)" ? draft.severity : (rule.severity || "Warning")} disabled={true}>
                {["Minimal", "Warning", "Moderate", "Severe", "Critical"].map((s) => <option key={s} value={s}>{s}</option>)}
              </select>
              <em className="rule-fld-note">Severity is set by the rule and can't be edited here.</em>
            </label>
            {canEdit && String(draft.severity) !== "Pass (override)" && (
              <button className="rule-override-btn" onClick={() => set("severity", "Pass (override)")}>
                <Icon name="shield-off" size={14} stroke={2.4} /> Override to Pass for this item
              </button>
            )}
            {String(draft.severity) === "Pass (override)" && (
              <div className="rule-override">
                <div className="rule-override-h"><Icon name="alert-triangle" size={14} stroke={2.4} /> Override to Pass — signature required</div>
                <p className="rule-override-p">Overriding forces this flag to Pass for this item. This is captured in the audit log with your full name, the timestamp, and your reason.</p>
                <label className="rule-fld"><span>Full name signature <b className="req">*</b></span>
                  <input className="rule-input" value={draft.ovSig || (state.userName || "")} disabled title="Your account name is used to sign — it cannot be changed" placeholder="Your full name" onChange={(e) => set("ovSig", e.target.value)} />
                </label>
                <label className="rule-fld"><span>Reason / note <b className="req">*</b></span>
                  <textarea className="rule-input" rows={2} value={draft.ovReason || ""} disabled={!canEdit} placeholder="Why is this override justified?" onChange={(e) => set("ovReason", e.target.value)} />
                </label>
                <div className="rule-override-date"><Icon name="calendar" size={12} /> Timestamp {new Date().toLocaleString()}</div>
                <button className="rule-override-cancel" onClick={() => { set("severity", rule.severity || "Warning"); set("ovSig", ""); set("ovReason", ""); }}>Cancel override</button>
              </div>
            )}
            {Array.isArray(rule.tags) && rule.tags.length > 0 && (
              <div className="rule-fld"><span>Mapped health tags</span><div className="rule-tags">{rule.tags.map((t) => <span key={t} className="pill neutral" style={{ fontSize: 11 }}>{t}</span>)}</div></div>
            )}
            {(() => {
              let limits = [];
              try { limits = (compLoad().limits || {})[rule.id] || []; } catch (e) {}
              if (!limits.length) return null;
              return (
                <div className="rule-fld">
                  <span>Threshold metrics</span>
                  <div className="rule-metrics">
                    <div className="rule-metrics-head"><span>Nutrient</span><span>Min</span><span>Max</span><span>Severity</span><span>Scope</span></div>
                    {limits.map((l, idx) => (
                      <div key={idx} className="rule-metrics-row">
                        <span className="rm-n">{l.nutrient}</span>
                        <span>{l.min != null && l.min !== "" ? l.min : "—"}</span>
                        <span className="rm-max">{l.max != null && l.max !== "" ? l.max : "—"}</span>
                        <span className={"rm-sev " + String(l.severity || "").toLowerCase()}>{l.severity || "—"}</span>
                        <span className="rm-scope">{l.scope || "recipe"}</span>
                      </div>
                    ))}
                  </div>
                </div>
              );
            })()}
            {(() => {
              // Rule education — explain what this rule checks and the science behind
              // each governed nutrient (from the shared nutrient knowledge base).
              let limits = [];
              try { limits = (compLoad().limits || {})[rule.id] || []; } catch (e) {}
              const EDU = (typeof window !== "undefined" && window.NUTRIENT_EDU) || {};
              const norm = (n) => String(n || "").replace(/\s*\(.*?\)\s*/g, "").replace(/\bfatty acid\b/i, "").trim();
              const alias = { "Total fat": "Total fat", "Saturated Fat": "Saturated", "Monounsaturated": "Monounsaturated", "Carbohydrates": "Carbohydrates", "Sugar": "Sugar", "Sodium": "Sodium", "Fibre": "Fibre", "Fiber": "Fibre", "Protein": "Protein", "Calories": "Calories" };
              const eduFor = limits.map((l) => { const key = alias[norm(l.nutrient)] || norm(l.nutrient); return EDU[key] ? { n: norm(l.nutrient), e: EDU[key] } : null; }).filter(Boolean);
              return (
                <div className="rule-edu">
                  <div className="rule-edu-h"><Icon name="book-open" size={13} stroke={2.2} /> Understanding this rule</div>
                  <p className="rule-edu-lead">
                    This is a <b>{(rule.type || (limits.length ? "nutrition" : "policy"))}</b> rule your organization enforces{rule.severity ? " at " + String(rule.severity).toLowerCase() + " severity" : ""}. When {kind === "allergens" ? "an ingredient contains the watched allergen" : "a recipe or ingredient falls outside the thresholds above"}, it's flagged in compliance review{String(rule.severity).toLowerCase() === "critical" ? " and blocked from publishing until resolved or an authorized override is signed" : " for a reviewer to check"}.
                  </p>
                  {eduFor.map((x, idx) => (
                    <div key={idx} className="rule-edu-n">
                      <div className="rule-edu-n-h">{x.n}</div>
                      <p><b>Why it's limited.</b> {x.e.why}{x.e.high ? " " + x.e.high : ""}</p>
                      {x.e.who && <p><b>Who it affects.</b> {x.e.who}</p>}
                    </div>
                  ))}
                  <div className="rule-edu-tip"><Icon name="shield-check" size={13} stroke={2.2} /><span>Thresholds are curated by your compliance team and reviewed against Health Canada, FDA and WHO guidance. {canEdit ? "As an admin you can adjust them above." : "Contact your Admin to propose a change."}</span></div>
                </div>
              );
            })()}
            <div className="rule-drawer-meta"><Icon name="hash" size={12} /> {rule.id}{rule.status ? " · " + rule.status : ""}</div>
          </div>
        )}

        {rule && (
          <div className="rule-drawer-foot">
            {canEdit ? (
              <React.Fragment>
                <button className="btn ghost" onClick={close}>Cancel</button>
                {state.canSave
                  ? <button className="btn primary" onClick={save}><Icon name="check" size={15} /> Save rule</button>
                  : <button className="btn primary" disabled title="Your role can edit but not save rule changes — ask an Admin to grant 'Save compliance rule changes'." style={{ opacity: .5, cursor: "not-allowed" }}><Icon name="lock" size={15} /> Save rule</button>}
              </React.Fragment>
            ) : (
              <React.Fragment>
                <button className="btn ghost" onClick={close}>Close</button>
                <button className="btn secondary" onClick={() => { try { window.__toast && window.__toast("A request to change this rule was sent to your Admin."); } catch (e) {} close(); }}><Icon name="mail" size={15} /> Contact Admin</button>
              </React.Fragment>
            )}
          </div>
        )}
      </div>
    </div>, document.body);
}
(function mountRuleDrawer() {
  try {
    var mount = function () {
      if (document.getElementById("__rule-drawer-root")) return;
      var el = document.createElement("div"); el.id = "__rule-drawer-root"; document.body.appendChild(el);
      ReactDOM.render(<RuleDrawer />, el);
    };
    if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", mount); else setTimeout(mount, 300);
  } catch (e) {}
})();

/* Lock notice shown on published items, read-only for everyone except Admin */
function PublishedLock({ canEdit }) {
  return (
    <div className={`wf-lock ${canEdit ? "admin" : ""}`}>
      <span className="wf-lock-ic"><Icon name={canEdit ? "shield" : "lock"} size={16} stroke={2.2} /></span>
      <div className="wf-lock-txt">
        <strong>{canEdit ? "Published & locked, Admin override" : "Published & locked"}</strong>
        <span>{canEdit
          ? "This content is live. As an Admin you can still edit or delete it; everyone else is read-only."
          : "Live content can’t be edited or deleted. Contact an Admin if a change is required."}</span>
      </div>
    </div>
  );
}
function wePatchItem(kind, id, patch) {
  const arr = kind === "ingredient" ? window.INGREDIENT_ITEMS : window.RECIPES;
  if (!arr) return;
  const it = arr.find((x) => x.id === id);
  if (it) Object.assign(it, patch);
}
