/* NutriDMS, Publishing Workflow (Settings · Admin)
   Configurable review/approval/publishing engine, separate per content type.
   Visual builder: add / remove / reorder / disable steps, set step type,
   assign a role/group/user, mark one final approver. Auto-publish toggle.
   A dynamic Status Engine + reviewer-queue + routing preview reflects config. */

const { useState: wfUseState, useEffect: wfUseEffect, useMemo: wfUseMemo } = React;

/* ── Assignee options: roles/groups first, then specific users ──
   Role labels come straight from the canonical ROLES map (data.js) so the
   dropdown always matches the naming used everywhere else in NutriDMS. */
function wfRoleOpts() {
  const kind = window.__nutridmsWorkflowEditorKind || "recipe";
  const options = (window.__nutridmsWorkflowAssignmentOptions || {})[kind] || {};
  const roles = Array.isArray(options.roles) ? options.roles : [];
  return roles.map(r => ({ id: "role:" + r.id, label: r.label, hint: "Role" }));
}
function wfAssigneeOpts() { return [{ id: "eligible", label: "Any eligible reviewer", hint: "System" }, ...wfRoleOpts()]; }
function wfUserOpts() {
  const kind = window.__nutridmsWorkflowEditorKind || "recipe";
  const options = (window.__nutridmsWorkflowAssignmentOptions || {})[kind] || {};
  const members = Array.isArray(options.members) ? options.members : [];
  return members.map(member => ({ id: "user:" + member.id, label: member.name, hint: member.role || "Member" }));
}
function wfAssigneeLabel(id) {
  const all = [...wfAssigneeOpts(), ...wfUserOpts()];
  const f = all.find(o => o.id === id);
  return f ? f.label : "Unassigned";
}

const WF_STEP_TYPES = {
  review:   { label: "Review",   ic: "clipboard-check", tone: "info",    actions: ["Approve", "Request Changes", "Escalate"], desc: "Validate content." },
  approval: { label: "Approval", ic: "gavel",           tone: "violet",  actions: ["Approve", "Reject", "Request Changes"],   desc: "Final business decision." },
};

const uid = () => "s-" + Math.random().toString(36).slice(2, 8);

const WF_DEFAULTS = {
  recipe: {
    name: "Recipe Publishing Workflow",
    status: "active",
    autoPublish: false,
    steps: [
      { id: uid(), name: "Review & approval", type: "approval", assignee: "eligible", sla: 24, disabled: false, final: true },
    ],
  },
  ingredient: {
    name: "Ingredient Publishing Workflow",
    status: "active",
    autoPublish: false,
    steps: [
      { id: uid(), name: "Review & approval", type: "approval", assignee: "eligible", sla: 24, disabled: false, final: true },
    ],
  },
};

function wfNormalize(value) {
  const source = value && typeof value === "object" ? value : {};
  const normalized = JSON.parse(JSON.stringify(WF_DEFAULTS));
  const definitions = Array.isArray(source.workflows) ? source.workflows : null;
  if (definitions) {
    definitions.forEach((definition) => {
      const kind = definition && definition.content_type;
      if (!normalized[kind]) return;
      const stages = Array.isArray(definition.stages) ? definition.stages : [];
      normalized[kind] = {
        ...normalized[kind], name: definition.name || normalized[kind].name,
        status: definition.status || "active", autoPublish: !!definition.auto_publish_after_approval,
        steps: stages.map((stage) => {
          const target = stage.assignment || {};
          const assignee = target.type === "role" ? "role:" + target.role_id
            : target.type === "member" ? "user:" + target.member_id : "eligible";
          return {
            id: stage.id || uid(), name: stage.name || "Review step",
            type: stage.stage_type || "review", assignee,
            sla: stage.sla_hours || 24, disabled: stage.is_active === false,
            final: !!stage.is_final,
          };
        }),
      };
    });
    return normalized;
  }
  ["recipe", "ingredient"].forEach((kind) => {
    const section = source[kind] || source[kind + "s"];
    if (!section || typeof section !== "object") return;
    const next = { ...normalized[kind], ...section };
    if (section.autoPublish == null && section.auto_publish_after_approval != null) {
      next.autoPublish = !!section.auto_publish_after_approval;
    }
    next.steps = Array.isArray(section.steps) ? section.steps : normalized[kind].steps;
    normalized[kind] = next;
  });
  return normalized;
}

function wfLoad() {
  return wfNormalize(window.__nutridmsServerWorkflows || null);
}

function wfApiPayload(wf) {
  return {
    name: wf.name,
    auto_publish_after_approval: !!wf.autoPublish,
    stages: wf.steps.map((step) => {
      const assignee = String(step.assignee || "eligible");
      const assignment = assignee.indexOf("role:") === 0
        ? { type: "role", role_id: assignee.slice(5) }
        : assignee.indexOf("user:") === 0
          ? { type: "member", member_id: assignee.slice(5) }
          : { type: "eligible" };
      return {
        name: step.name, stage_type: step.type, assignment,
        sla_hours: Number(step.sla) || 24, is_active: !step.disabled, is_final: !!step.final,
      };
    }),
  };
}

/* ── Derived: statuses generated from active steps ── */
function wfStatuses(wf) {
  const active = wf.steps.filter(s => !s.disabled);
  const out = active.map(s => "Pending " + s.name);
  out.push("Changes Requested");
  if (!wf.autoPublish) out.push("Ready for Publishing");
  out.push("Published");
  out.push("Rejected");
  return out;
}

/* ════════════════════════════════════════════════════════════ */
function SetPublishingWorkflow({ toast }) {
  const [ct, setCt] = wfUseState("recipe");          // recipe | ingredient
  const [all, setAll] = wfUseState(wfLoad);
  const [dirty, setDirty] = wfUseState(false);
  const [saving, setSaving] = wfUseState(false);
  const [autoAssignByLoad, setAutoAssignByLoad] = wfUseState(true);
  const [requireSeparateApprover, setRequireSeparateApprover] = wfUseState(true);
  const canManageStages = ["admin", "super-admin"].includes(window.__role || "");
  const wf = all[ct];

  wfUseEffect(() => {
    let active = true;
    (async () => {
      try {
        const client = window.NutriPublishingWorkflows;
        if (!client || typeof client.list !== "function") return;
        const remote = await client.list();
        if (!active || !remote) return;
        const normalized = wfNormalize(remote);
        const policies = remote.policies || {};
        setAutoAssignByLoad(policies.auto_assign_by_load !== false);
        setRequireSeparateApprover(policies.require_separate_approver !== false);
        setAll(normalized);
        window.__nutridmsServerWorkflows = remote;
        const kinds = ["recipe", "ingredient"];
        const options = await Promise.all(kinds.map(kind => client.assignmentOptions(kind)));
        window.__nutridmsWorkflowAssignmentOptions = { recipe: options[0], ingredient: options[1] };
      } catch (e) {}
    })();
    return () => { active = false; };
  }, []);

  wfUseEffect(() => { window.__nutridmsWorkflowEditorKind = ct; }, [ct]);

  const mutate = (fn) => { if (!canManageStages) return; setAll(a => ({ ...a, [ct]: fn(a[ct]) })); setDirty(true); };
  const setWf  = (patch) => mutate(w => ({ ...w, ...patch }));
  const setStep = (id, patch) => mutate(w => ({ ...w, steps: w.steps.map(s => s.id === id ? { ...s, ...patch } : s) }));

  const addStep = () => mutate(w => ({
    ...w,
    steps: [...w.steps, { id: uid(), name: "New Review Step", type: "review", assignee: "eligible", sla: 24, disabled: false, final: false }],
  }));
  const removeStep = (id) => mutate(w => ({ ...w, steps: w.steps.filter(s => s.id !== id) }));
  const moveStep = (id, dir) => mutate(w => {
    const i = w.steps.findIndex(s => s.id === id); const j = i + dir;
    if (i < 0 || j < 0 || j >= w.steps.length) return w;
    const steps = w.steps.slice(); const [it] = steps.splice(i, 1); steps.splice(j, 0, it);
    return { ...w, steps };
  });
  const markFinal = (id) => mutate(w => ({ ...w, steps: w.steps.map(s => ({ ...s, final: s.id === id })) }));

  const save = async () => {
    if (saving || !canManageStages) return;
    setSaving(true);
    try {
      const client = window.NutriPublishingWorkflows;
      if (!client || typeof client.save !== "function") throw new Error("Publishing workflow service is unavailable.");
      await client.save(ct, wfApiPayload(all[ct]));
      await client.savePolicies({
        auto_assign_by_load: autoAssignByLoad,
        require_separate_approver: requireSeparateApprover,
      });
      const remote = await client.list();
      const normalized = wfNormalize(remote);
      setAll(normalized);
      window.__nutridmsServerWorkflows = remote;
      setDirty(false);
      toast && toast(ct === "recipe" ? "Recipe workflow saved" : "Ingredient workflow saved");
    } catch (error) {
      toast && toast((error && error.message) || "Publishing workflow could not be saved.");
    } finally {
      setSaving(false);
    }
  };
  const reset = () => { if (!canManageStages) return; setAll(JSON.parse(JSON.stringify(WF_DEFAULTS))); setAutoAssignByLoad(true); setRequireSeparateApprover(true); setDirty(true); };

  const statuses = wfUseMemo(() => wfStatuses(wf), [wf]);
  const finalStep = wf.steps.find(s => s.final && !s.disabled);
  const activeSteps = wf.steps.filter(s => !s.disabled);
  const needsRepair = wf.status === "repair_required";

  return (
    <div className="enterprise-workflow-settings">
      {/* Intro */}
      <div className="card pad" style={{ marginBottom: 16, borderLeft: "3px solid var(--green-600)" }}>
        <div style={{ display: "flex", gap: 9 }}>
          <Icon name="git-branch" size={16} style={{ color: "var(--green-700)", flexShrink: 0, marginTop: 1 }} />
          <p className="muted" style={{ fontSize: 13, margin: 0, lineHeight: 1.5 }}>
            These server-enforced policies govern review assignment and publishing for the active organization. Contributors cannot approve their own submissions when separate approval is enabled. <strong style={{ color: "var(--text-primary)" }}>Admin only.</strong>
          </p>
        </div>
      </div>

      {/* Content-type switch */}
      <div className="wf-ct">
        {[["recipe", "Recipe Workflow", "utensils-crossed"], ["ingredient", "Ingredient Workflow", "leaf"]].map(([id, label, ic]) => (
          <button key={id} className={`wf-ct-tab ${ct === id ? "on" : ""}`} onClick={() => setCt(id)}>
            <Icon name={ic} size={15} stroke={2.2} /> {label}
            <span className={`wf-ct-status ${all[id].status}`}>{all[id].status === "repair_required" ? "Repair required" : all[id].status === "active" ? "Active" : "Draft"}</span>
          </button>
        ))}
      </div>

      {/* Workflow header config */}
      <div style={{ opacity: canManageStages ? 1 : 0.62, pointerEvents: canManageStages ? "auto" : "none" }} aria-disabled={!canManageStages}>
      <div className="card pad wf-cfg">
        {needsRepair && <div className="alert error" style={{ marginBottom: 14 }}>This workflow contains a stage whose role was retired. Save a valid stage configuration before new submissions can enter it.</div>}
        <div className="wf-cfg-grid">
          <div className="field">
            <label>Workflow name</label>
            <input className="input" value={wf.name} disabled={!canManageStages} onChange={e => setWf({ name: e.target.value })} />
          </div>
          <div className="field"><label>Workflow status</label><div className="input" style={{ display: "flex", alignItems: "center" }}>{needsRepair ? "Repair required · new submissions paused" : "Active · server enforced"}</div></div>
        </div>
        <div className="wf-autopub">
          <div>
            <div className="wf-autopub-t">Auto-publish on final approval</div>
            <div className="wf-autopub-d">
              {wf.autoPublish
                ? "Final approval publishes content immediately."
                : "Final approval moves content to “Ready for Publishing” for a manual publish."}
            </div>
          </div>
          <button className={`la-toggle ${wf.autoPublish ? "on" : ""}`} disabled={!canManageStages} onClick={() => setWf({ autoPublish: !wf.autoPublish })}><i /></button>
        </div>
        <div className="wf-autopub">
          <div>
            <div className="wf-autopub-t">Require a separate approver</div>
            <div className="wf-autopub-d">Blocks submitters from claiming, requesting changes on, or approving their own content.</div>
          </div>
          <button className={`la-toggle ${requireSeparateApprover ? "on" : ""}`} disabled={!canManageStages} onClick={() => { setRequireSeparateApprover((value) => !value); setDirty(true); }}><i /></button>
        </div>
      </div>

      {/* Visual builder */}
      <div className="wf-warn" style={{ marginBottom: 12 }}><Icon name="shield-check" size={14} stroke={2.4} /> Changes are validated and enforced by the server for new submissions. Existing reviews keep the workflow version they entered.</div>
      <div className="wf-builder-head">
        <h3>Workflow builder</h3>
        <button className="btn secondary sm" onClick={addStep}><Icon name="plus" size={14} stroke={2.4} /> Add step</button>
      </div>

      <div className="wf-canvas">
        {/* fixed start */}
        <div className="wf-node start">
          <div className="wf-node-ic"><Icon name="user-round" size={16} stroke={2.2} /></div>
          <div><div className="wf-node-t">Contributor</div><div className="wf-node-d">Submits for review</div></div>
        </div>

        {wf.steps.length === 0 && (
          <>
            <WfConn />
            <div className="wf-empty">No review steps, content publishes straight after submission. <button className="btn ghost sm" onClick={addStep}>Add a step</button></div>
          </>
        )}

        {wf.steps.map((s, i) => (
          <React.Fragment key={s.id}>
            <WfConn />
            <WfStepCard
              step={s} index={i} count={wf.steps.length}
              onChange={(p) => setStep(s.id, p)}
              onRemove={() => removeStep(s.id)}
              onMove={(d) => moveStep(s.id, d)}
              onFinal={() => markFinal(s.id)}
            />
          </React.Fragment>
        ))}

        <WfConn />
        {/* fixed end */}
        <div className="wf-node end">
          <div className="wf-node-ic"><Icon name="globe" size={16} stroke={2.2} /></div>
          <div>
            <div className="wf-node-t">{wf.autoPublish ? "Published" : "Ready for Publishing"}</div>
            <div className="wf-node-d">{wf.autoPublish ? "Live automatically after final approval" : "Manual publish after final approval → Published"}</div>
          </div>
          <span className="wf-sys-pill"><Icon name="lock" size={11} stroke={2.4} /> System</span>
        </div>
      </div>

      {/* Validation */}
      {!finalStep && activeSteps.length > 0 && (
        <div className="wf-warn"><Icon name="alert-triangle" size={14} stroke={2.4} /> No final approver set. Mark one active step as the final approval step to control publishing.</div>
      )}

      {/* Preview: status engine + routing */}
      <div className="wf-preview">
        <div className="wf-prev-col">
          <div className="wf-prev-h"><Icon name="tags" size={14} stroke={2.2} /> Generated statuses</div>
          <p className="wf-prev-sub">Auto-derived from the steps above.</p>
          <div className="wf-status-list">
            {statuses.map((st, i) => (
              <span key={i} className={`wf-status-chip ${st === "Published" ? "pub" : st === "Rejected" ? "rej" : st === "Changes Requested" ? "chg" : st === "Ready for Publishing" ? "ready" : ""}`}>{st}</span>
            ))}
          </div>
        </div>

        <div className="wf-prev-col">
          <div className="wf-prev-h"><Icon name="inbox" size={14} stroke={2.2} /> Reviewer queues</div>
          <p className="wf-prev-sub">Each reviewer sees only their step.</p>
          <div className="wf-queue-list">
            {activeSteps.length === 0 && <div className="muted" style={{ fontSize: 12.5 }}>No queues, nothing to review.</div>}
            {activeSteps.map((s) => (
              <div key={s.id} className="wf-queue-row">
                <span className={`wf-type-dot ${WF_STEP_TYPES[s.type].tone}`} />
                <span className="wf-queue-name">{s.name}{s.final && <span className="wf-final-tag">Final</span>}</span>
                <span className="wf-queue-meta">{wfAssigneeLabel(s.assignee)}</span>
              </div>
            ))}
          </div>
        </div>
      </div>

      <div className="wf-routing">
        <Icon name="route" size={14} stroke={2.2} />
        <span>
          On <strong>Submit for Review</strong>, content enters{" "}
          <strong>{activeSteps[0] ? "Pending " + activeSteps[0].name : (wf.autoPublish ? "Published" : "Ready for Publishing")}</strong>.
          Approving a step routes to the next automatically. Requesting changes returns it to{" "}
          <strong>Changes Requested</strong>, and a resubmit comes back to the same step, never restarting the flow.
        </span>
      </div>
      <div className="st-foot" style={{ marginTop: 18 }}>
        <button className="btn ghost" onClick={reset}>Reset to defaults</button>
        <button className="btn primary" disabled={!dirty || saving} onClick={save}>
          <Icon name="check" size={13} stroke={2.4} /> {saving ? "Saving…" : dirty ? "Save workflow" : "Saved"}
        </button>
      </div>
      </div>
    </div>
  );
}

function WfConn() {
  return <div className="wf-conn"><span className="wf-conn-line" /><Icon name="chevron-down" size={16} stroke={2.4} /></div>;
}

function WfStepCard({ step, index, count, onChange, onRemove, onMove, onFinal }) {
  const t = WF_STEP_TYPES[step.type];
  const userOpts = wfUserOpts();
  return (
    <div className={`wf-step ${step.disabled ? "off" : ""} ${step.final ? "final" : ""}`}>
      <div className="wf-step-rail">
        <button className="wf-mini" disabled={index === 0} onClick={() => onMove(-1)} title="Move up"><Icon name="chevron-up" size={14} stroke={2.6} /></button>
        <span className="wf-step-num">{index + 1}</span>
        <button className="wf-mini" disabled={index === count - 1} onClick={() => onMove(1)} title="Move down"><Icon name="chevron-down" size={14} stroke={2.6} /></button>
      </div>

      <div className="wf-step-main">
        <div className="wf-step-top">
          <span className={`wf-type-badge ${t.tone}`}><Icon name={t.ic} size={12} stroke={2.4} /> {t.label}</span>
          {step.final && <span className="wf-final-badge"><Icon name="award" size={11} stroke={2.4} /> Final approver</span>}
          {step.disabled && <span className="wf-off-badge">Disabled</span>}
          <div className="wf-step-tools">
            <button className="wf-mini" onClick={() => onChange({ disabled: !step.disabled })} title={step.disabled ? "Enable" : "Disable"}>
              <Icon name={step.disabled ? "eye-off" : "eye"} size={14} stroke={2.2} />
            </button>
            <button className="wf-mini danger" disabled={count <= 1} onClick={onRemove} title="Remove step"><Icon name="trash-2" size={14} stroke={2.2} /></button>
          </div>
        </div>

        <input className="wf-step-name" value={step.name} onChange={e => onChange({ name: e.target.value })} placeholder="Step name" />

        <div className="wf-step-fields">
          <div className="wf-field">
            <label>Step type</label>
            <div className="wf-seg sm">
              {Object.entries(WF_STEP_TYPES).map(([k, v]) => (
                <button key={k} className={step.type === k ? "on" : ""} onClick={() => onChange({ type: k })}>{v.label}</button>
              ))}
            </div>
          </div>
          <div className="wf-field">
            <label>Assigned to</label>
            <select className="select" value={step.assignee} onChange={e => onChange({ assignee: e.target.value })}>
              <optgroup label="Eligible roles">
                {wfAssigneeOpts().map(o => <option key={o.id} value={o.id}>{o.label}</option>)}
              </optgroup>
              <optgroup label="Specific user">
                {userOpts.map(o => <option key={o.id} value={o.id}>{o.label} · {o.hint}</option>)}
              </optgroup>
            </select>
          </div>
          <div className="wf-field sla">
            <label>Response time (hrs)</label>
            <input className="input" type="number" min="1" value={step.sla} onChange={e => onChange({ sla: Math.max(1, +e.target.value || 1) })} />
          </div>
        </div>

        <div className="wf-step-foot">
          <div className="wf-actions">
            <span className="wf-actions-lbl">Actions:</span>
            {t.actions.map(a => <span key={a} className="wf-action-chip">{a}</span>)}
          </div>
          <label className="wf-final-check">
            <input type="checkbox" checked={!!step.final} disabled={step.disabled} onChange={onFinal} />
            <span>Final approval step</span>
          </label>
        </div>
      </div>
    </div>
  );
}

Object.assign(window, { SetPublishingWorkflow, wfLoad, wfAssigneeLabel, wfAssigneeOpts, WF_STEP_TYPES, WF_DEFAULTS, wfSubmitInfo });

/* ── Dynamic submission target for a content type, from the org's workflow ──
   Returns the first active step (entry queue), a human status label, the team
   role that owns it, and the list of specific reviewers a contributor can pick. */
function wfSubmitInfo(kind) {
  const selectedKind = kind === "ingredient" ? "ingredient" : "recipe";
  const raw = window.__nutridmsServerWorkflows || {};
  const definition = (raw.workflows || []).find(w => w.content_type === selectedKind);
  const first = definition && Array.isArray(definition.stages)
    ? definition.stages.find(stage => stage.is_active !== false) : null;
  const target = first && first.assignment || {};
  const options = (window.__nutridmsWorkflowAssignmentOptions || {})[selectedKind] || {};
  const allMembers = Array.isArray(options.members) ? options.members : [];
  const reviewers = target.type === "member"
    ? allMembers.filter(member => String(member.id) === String(target.member_id))
    : allMembers;
  const teamLabel = target.type === "member" ? (target.member_name || "the assigned reviewer")
    : target.type === "role" ? (target.role_label || "the selected role") : "any eligible reviewer";
  const stepName = first ? first.name : (definition && definition.auto_publish_after_approval ? "Published" : "Ready for Publishing");
  return {
    workflowName: definition && definition.name, stepName, statusLabel: first ? "Pending " + first.name : stepName,
    teamRole: target.type === "role" ? target.role_id : null, teamLabel,
    reviewers: reviewers.map(member => ({ id: member.id, initials: member.id, name: member.name, role: member.role })),
    hasWorkflow: !!definition,
  };
}
