/* approval-tab.jsx — Tab "Phê duyệt" trong Tài liệu (thiết kế kiểu Ventus: stepper ngang).
   Chạy bằng luồng + phiên THẬT: cdeApproval (luồng tạo ở Quản trị → Phê duyệt) + cdeApprovalRun.
   window.ApprovalTab. */
(function () {
  const { useState, useEffect } = React;
  const Icon = window.Icon;

  const STEP_STATE = {
    done:   { c: "var(--ok)", ic: "check" },
    active: { c: "var(--accent)", ic: "clock" },
    wait:   { c: "var(--ink-4)", ic: "clock" },
    reject: { c: "var(--danger)", ic: "alert" },
  };
  const fmtDate = (iso) => {
    if (!iso) return "";
    try { const d = new Date(iso); return ("0" + d.getDate()).slice(-2) + "/" + ("0" + (d.getMonth() + 1)).slice(-2) + " " + ("0" + d.getHours()).slice(-2) + ":" + ("0" + d.getMinutes()).slice(-2); }
    catch { return ""; }
  };
  const stChip = (s) => s === "approved" ? ["ok", "Đã duyệt"] : s === "rejected" ? ["danger", "Từ chối"] : s === "cancelled" ? ["idle", "Đã huỷ"] : ["warn", "Đang duyệt"];

  function ApprovalCard({ inst, roleName, canActOn, onAdvance, onCancel, canManage }) {
    const cfg = inst.config || {};
    const nodes = cfg.nodes || [];
    const gateOf = (n) => (cfg.gates || []).find(g => g.id === n.gateId) || {};
    const hist = inst.history || [];
    const lastHist = hist[hist.length - 1];
    const histNode = new Set(hist.map(h => h.node));
    const curGate = (() => { const n = nodes.find(x => x.key === inst.currentNode); return n ? gateOf(n) : null; })();
    const [cls, lbl] = stChip(inst.status);

    const steps = nodes.map(n => {
      const g = gateOf(n);
      let state;
      if (inst.status === "running") state = (n.key === inst.currentNode) ? "active" : (histNode.has(n.key) ? "done" : "wait");
      else if (inst.status === "approved") state = "done";
      else state = (lastHist && lastHist.node === n.key) ? "reject" : (histNode.has(n.key) ? "done" : "wait"); // rejected/cancelled
      const h = [...hist].reverse().find(x => x.node === n.key);
      const roleNames = (g.roles || []).map(roleName).filter(Boolean);
      return { key: n.key, label: g.name || n.key, color: g.color, state, who: roleNames.length ? roleNames.join(", ") : "Editor/Admin", date: h ? fmtDate(h.at) : "", note: (state === "reject" && lastHist) ? lastHist.outcome : "" };
    });

    const active = inst.status === "running";
    const mayAct = active && canActOn(inst);
    const outcomes = (curGate && curGate.outcomes) || [];

    return (
      <div className="card card-pad">
        <div className="card-head" style={{ marginBottom: 16, display: "flex", alignItems: "center", justifyContent: "space-between" }}>
          <div style={{ display: "flex", alignItems: "center", gap: 11, minWidth: 0 }}>
            <span style={{ width: 38, height: 38, borderRadius: 11, flex: "none", display: "grid", placeItems: "center", background: "var(--accent-soft)", color: "var(--accent)" }}><Icon.flow size={19} /></span>
            <div style={{ minWidth: 0 }}>
              <div style={{ fontWeight: 800, fontSize: 14.5, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{inst.docName}</div>
              <div style={{ fontSize: 12, color: "var(--ink-3)", fontWeight: 500, marginTop: 2 }}>{inst.wfName}{inst.disc ? " · " + inst.disc : ""}</div>
            </div>
          </div>
          <div style={{ display: "flex", alignItems: "center", gap: 8, flex: "none" }}>
            <span className={"chip " + cls} style={{ height: 24 }}>{lbl}</span>
            {canManage && active && <button className="icon-btn" title="Thu hồi khỏi luồng" style={{ width: 30, height: 30, borderRadius: 8 }} onClick={() => onCancel(inst)}><Icon.ban size={15} /></button>}
          </div>
        </div>

        {/* stepper ngang */}
        <div style={{ display: "flex", alignItems: "flex-start", overflowX: "auto", paddingBottom: 4 }}>
          {steps.map((s, i) => {
            const ss = STEP_STATE[s.state]; const Ico = Icon[ss.ic];
            return (
              <React.Fragment key={s.key}>
                <div style={{ display: "flex", flexDirection: "column", alignItems: "center", gap: 8, flex: "none", width: 118, textAlign: "center" }}>
                  <span style={{ width: 34, height: 34, borderRadius: 99, display: "grid", placeItems: "center", flex: "none",
                    background: s.state === "wait" ? "var(--sunken)" : ss.c, color: s.state === "wait" ? "var(--ink-4)" : "#fff",
                    boxShadow: s.state === "active" ? "0 0 0 4px var(--accent-soft)" : "none" }}><Ico size={16} /></span>
                  <div style={{ display: "flex", flexDirection: "column", gap: 2 }}>
                    <div style={{ fontSize: 11, fontWeight: 700, color: s.state === "wait" ? "var(--ink-4)" : "var(--ink)", lineHeight: 1.3 }}>{s.label}</div>
                    <div style={{ fontSize: 10.5, color: "var(--ink-3)", fontWeight: 600, lineHeight: 1.3 }}>{s.who}</div>
                    {s.date && <div style={{ fontSize: 10, color: "var(--ink-4)", fontWeight: 600 }}>{s.date}</div>}
                    {s.note && <div style={{ fontSize: 10.5, color: "var(--danger)", fontWeight: 700 }}>{s.note}</div>}
                  </div>
                </div>
                {i < steps.length - 1 && <div style={{ flex: 1, minWidth: 24, height: 2, background: steps[i + 1].state === "wait" ? "var(--line)" : (steps[i].state === "reject" ? "var(--danger)" : "var(--ok)"), marginTop: 17 }} />}
              </React.Fragment>
            );
          })}
        </div>

        {/* callout + hành động cổng hiện tại */}
        <div style={{ marginTop: 18, padding: "12px 14px", borderRadius: 11, display: "flex", alignItems: "center", gap: 11, flexWrap: "wrap",
          background: inst.status === "approved" ? "rgba(16,185,129,.10)" : (inst.status === "rejected" || inst.status === "cancelled") ? "rgba(239,68,68,.08)" : "var(--sunken)" }}>
          <Icon.bolt size={16} color={inst.status === "approved" ? "var(--ok)" : (inst.status === "rejected" || inst.status === "cancelled") ? "var(--danger)" : "var(--accent)"} />
          <div style={{ fontSize: 12.5, fontWeight: 600, color: "var(--ink-2)", lineHeight: 1.45, flex: 1, minWidth: 180 }}>
            {inst.status === "approved" ? <>Đã <b style={{ color: "var(--ok)" }}>hoàn tất phê duyệt</b> — các hành động cấu hình ở cổng đã được áp dụng.</>
              : inst.status === "rejected" ? <>Đã <b style={{ color: "var(--danger)" }}>từ chối</b> tại bước cuối.</>
              : inst.status === "cancelled" ? <>Phiên đã bị <b>thu hồi</b>.</>
              : <>Đang chờ duyệt tại cổng <b>{curGate ? curGate.name : "—"}</b>. Chọn kết quả để chuyển cổng (áp dụng hành động đã cấu hình).</>}
          </div>
          {active && (mayAct
            ? <div style={{ display: "flex", gap: 8, flexWrap: "wrap" }}>{outcomes.map(o => (
                <button key={o.key} className="btn sm" style={{ borderColor: o.color, color: o.color, fontWeight: 800 }} onClick={() => onAdvance(inst, o.key)}>{o.label}</button>
              ))}</div>
            : <span style={{ fontSize: 11.5, fontWeight: 700, color: "var(--ink-3)", fontStyle: "italic" }}>Chờ vai trò được phân duyệt</span>)}
        </div>
      </div>
    );
  }

  function ApprovalTab() {
    const auth = (typeof useAuthCtx === "function") ? useAuthCtx() : null;
    const role = (auth && auth.profile && auth.profile.role) || (auth && auth.user ? "viewer" : "admin");
    const canManage = role === "editor" || role === "admin";
    const [insts, setInsts] = useState([]);
    const [active, setActive] = useState(null);
    const [docs, setDocs] = useState([]);
    const [roles, setRoles] = useState([]);
    const [myRoles, setMyRoles] = useState([]);
    const [open, setOpen] = useState(false);
    const [pick, setPick] = useState("");
    const [busy, setBusy] = useState(false);

    const reload = () => {
      if (window.cdeApprovalRun) window.cdeApprovalRun.instances().then(setInsts).catch(() => {});
      if (window.cdeApproval) window.cdeApproval.list().then(l => setActive((l || []).find(w => w.active) || null)).catch(() => {});
      if (window.cdeData) window.cdeData.containers().then(setDocs).catch(() => {});
      if (window.cdeRoles) { window.cdeRoles.list().then(r => setRoles(r || [])).catch(() => {}); window.cdeRoles.myRoleIds().then(setMyRoles).catch(() => {}); }
    };
    useEffect(reload, []);
    useEffect(() => { const h = () => reload(); window.addEventListener("cde-approval-changed", h); return () => window.removeEventListener("cde-approval-changed", h); }, []);
    const ping = () => { try { window.dispatchEvent(new Event("cde-approval-changed")); } catch (e) {} };

    const roleName = (id) => (roles.find(r => r.id === id) || {}).name || "";
    const gateOf = (inst) => { const cfg = inst.config || {}; const n = (cfg.nodes || []).find(x => x.key === inst.currentNode); return n && (cfg.gates || []).find(g => g.id === n.gateId); };
    const canActOn = (inst) => {
      if (role === "admin") return true;
      if (role !== "editor") return false;
      const gr = (gateOf(inst) || {}).roles || [];
      if (!gr.length) return true;
      return gr.some(id => myRoles.includes(id));
    };

    const inApproval = new Set(insts.filter(i => i.status === "running").map(i => i.containerId));
    const submit = async () => {
      if (!pick || !active) return; setBusy(true);
      try { await window.cdeApprovalRun.submit(pick, active.id); setPick(""); setOpen(false); reload(); ping(); }
      catch (e) { alert("Gửi duyệt thất bại: " + (e.message || e)); } finally { setBusy(false); }
    };
    const advance = async (inst, ok) => { try { await window.cdeApprovalRun.advance(inst.id, ok, (auth && auth.profile && auth.profile.full_name) || "Tôi"); reload(); ping(); } catch (e) { alert(e.message || e); } };
    const cancel = async (inst) => { if (!window.confirm('Thu hồi "' + inst.docName + '" khỏi luồng phê duyệt?')) return; try { await window.cdeApprovalRun.cancel(inst.id, (auth && auth.profile && auth.profile.full_name) || "Tôi"); reload(); ping(); } catch (e) { alert(e.message || e); } };

    const running = insts.filter(i => i.status === "running").length;
    const approved = insts.filter(i => i.status === "approved").length;
    const rejected = insts.filter(i => i.status === "rejected").length;
    const stats = [
      ["Đang chờ duyệt", running, "var(--warn,#f59e0b)", "clock"],
      ["Đã duyệt", approved, "var(--ok)", "checkCircle"],
      ["Từ chối", rejected, "var(--danger)", "alert"],
      ["Tổng phiên", insts.length, "var(--accent)", "flow"],
    ];

    return (
      <div className="content aptab" style={{ overflowY: "auto" }}>
        <div className="ae2-lib-head">
          <div>
            <div className="card-title">Phê duyệt tài liệu</div>
            <div className="card-sub">Chạy theo luồng tạo ở <b>Quản trị → Phê duyệt</b>{active ? <> · luồng đang dùng: <b>{active.name}</b></> : <> · <span style={{ color: "var(--danger)" }}>chưa có luồng kích hoạt</span></>}</div>
          </div>
          {canManage && active && <button className="btn sm primary" onClick={() => setOpen(o => !o)}><Icon.send size={14} /> Gửi duyệt</button>}
        </div>

        {open && canManage && active && (
          <div className="card card-pad" style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 16, flexWrap: "wrap" }}>
            <select className="ae2-sel" style={{ maxWidth: 320 }} value={pick} onChange={(e) => setPick(e.target.value)}>
              <option value="">— Chọn tài liệu để gửi duyệt —</option>
              {docs.filter(d => !inApproval.has(d.id)).map(d => <option key={d.id} value={d.id}>{d.name}</option>)}
            </select>
            <span style={{ fontSize: 13, fontWeight: 700, color: "var(--ink-3)" }}><Icon.chevR size={13} /> {active.name}</span>
            <button className="btn sm primary" disabled={!pick || busy} onClick={submit}>{busy ? "…" : "Gửi duyệt"}</button>
          </div>
        )}

        <div className="grid g-4" style={{ display: "grid", gridTemplateColumns: "repeat(4,1fr)", gap: 14, marginBottom: 18 }}>
          {stats.map(([l, v, c, ic], i) => { const Ico = Icon[ic]; return (
            <div key={i} className="card" style={{ display: "flex", flexDirection: "row", alignItems: "center", gap: 14, padding: 16 }}>
              <div style={{ width: 42, height: 42, borderRadius: 12, display: "grid", placeItems: "center", flex: "none", background: "color-mix(in srgb," + c + " 14%,transparent)", color: c }}><Ico size={20} /></div>
              <div><div style={{ fontSize: 26, fontWeight: 800 }}>{v}</div><div style={{ fontSize: 12.5, color: "var(--ink-3)", fontWeight: 600 }}>{l}</div></div>
            </div>
          ); })}
        </div>

        {!insts.length && (
          <div className="rl-empty"><Icon.checkCircle size={28} /><div>Chưa có tài liệu nào trong luồng phê duyệt.</div>{canManage && active && <div style={{ fontSize: 12.5, color: "var(--ink-3)" }}>Bấm "Gửi duyệt" để đưa tài liệu vào luồng <b>{active.name}</b>.</div>}</div>
        )}

        <div style={{ display: "flex", flexDirection: "column", gap: 16 }}>
          {insts.map(inst => (
            <ApprovalCard key={inst.id} inst={inst} roleName={roleName} canActOn={canActOn} onAdvance={advance} onCancel={cancel} canManage={canManage} />
          ))}
        </div>
      </div>
    );
  }

  window.ApprovalTab = ApprovalTab;
})();
