/* ============================================================
   org-view.jsx — per-user "organize by property" UI: virtual tree + config panel
   Depends on org-tree.js globals (ORG_FIELDS, organizableProps), Icon, React.
   ============================================================ */

/* ---------- virtual tree (mirrors the folder TreeNode look) ---------- */
// icon of the PROPERTY a node belongs to (system-field icon, or a custom property's chosen icon)
function orgFieldIcon(field, propsById) {
  if (!field) return null;
  if (field === "disc" || field === "status" || field === "type") return window.sysFieldMeta ? window.sysFieldMeta(field).icon : null;
  const p = propsById && propsById[field];
  return p && p.icon ? p.icon : null;
}
function VirtualTreeNode({ node, sel, onSelect, depth, propsById, expanded, onToggle, visible, onToggleVisible }) {
  const hasKids = !!(node.children && node.children.length);
  const open = hasKids && expanded.has(node.key);
  const active = sel && sel.key === node.key;
  const isFile = !!node.isFile;
  const vis = !isFile || !visible || visible[node.fileId] !== false;
  const discCol = (window.DISC_COL || {})[node.disc] || "var(--ink-3)";
  return (
    <div>
      <div onClick={() => onSelect && onSelect(node)}
        style={{ display: "flex", alignItems: "center", gap: 7, cursor: "pointer", padding: "7px 8px",
          paddingLeft: 8 + depth * 14, borderRadius: 8, marginBottom: 1,
          background: active ? "var(--accent-soft)" : "transparent", opacity: isFile ? (vis ? 1 : 0.45) : (node.count ? 1 : 0.5),
          color: active ? "var(--accent)" : "var(--ink-2)", fontSize: 13, fontWeight: active ? 700 : 600 }}>
        {hasKids ? (
          <span onClick={(e) => { e.stopPropagation(); onToggle(node.key); }}
            style={{ display: "grid", placeItems: "center", width: 18, height: 18, marginLeft: -3, marginRight: -4, flex: "none", cursor: "pointer" }}>
            <Icon.chevR size={13} color={active ? "var(--accent)" : "var(--ink-4)"}
              style={{ transform: open ? "rotate(90deg)" : "none", transition: "transform .2s cubic-bezier(.2,.8,.2,1)" }} />
          </span>
        ) : <span style={{ width: 13, flex: "none" }} />}
        {isFile ? (
          <Icon.doc size={16} color={discCol} style={{ flex: "none" }} />
        ) : (() => {
          const ic = orgFieldIcon(node.field, propsById);
          if (ic && window.PropIcon) return <span style={{ display: "flex", flex: "none", color: node.color || (active ? "var(--accent)" : "var(--ink-3)") }}>{React.createElement(window.PropIcon, { name: ic, size: 17 })}</span>;
          if (node.color) return <span style={{ width: 11, height: 11, borderRadius: 3, background: node.color, flex: "none" }} />;
          return <Icon.layers size={17} color={active ? "var(--accent)" : "var(--ink-3)"} style={{ flex: "none" }} />;
        })()}
        <span style={{ flex: 1, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{node.label}</span>
        {isFile && onToggleVisible ? (
          <span onClick={(e) => { e.stopPropagation(); onToggleVisible(node); }} title={vis ? "Ẩn trong 3D" : "Hiện trong 3D"}
            style={{ display: "grid", placeItems: "center", width: 22, height: 22, flex: "none", cursor: "pointer", color: vis ? "var(--ink-3)" : "var(--ink-4)" }}>
            {vis ? <Icon.eye size={15} /> : <Icon.eyeOff size={15} />}
          </span>
        ) : (node.count != null ? <span style={{ fontSize: 11, fontWeight: 700, color: "var(--ink-4)" }}>{node.count}</span> : null)}
      </div>
      {hasKids && (
        <div style={{ display: "grid", gridTemplateRows: open ? "1fr" : "0fr", transition: "grid-template-rows .2s cubic-bezier(.2,.8,.2,1)" }}>
          <div style={{ overflow: "hidden", minHeight: 0 }}>
            {node.children.map(c => <VirtualTreeNode key={c.key} node={c} sel={sel} onSelect={onSelect} depth={depth + 1} propsById={propsById} expanded={expanded} onToggle={onToggle} visible={visible} onToggleVisible={onToggleVisible} />)}
          </div>
        </div>
      )}
    </div>
  );
}
function collectBranchKeys(nodes, acc) {
  for (const n of nodes || []) if (n.children && n.children.length) { acc.push(n.key); collectBranchKeys(n.children, acc); }
  return acc;
}
function VirtualTree({ nodes, sel, onSelect, propsById, visible, onToggleVisible }) {
  const [expanded, setExpanded] = React.useState(() => new Set());   // default: thu gọn tất cả
  const onToggle = (key) => setExpanded(s => { const n = new Set(s); n.has(key) ? n.delete(key) : n.add(key); return n; });
  if (!nodes || !nodes.length) return <div style={{ padding: 16, fontSize: 12.5, color: "var(--ink-4)" }}>Không có hồ sơ phù hợp.</div>;
  const branchKeys = collectBranchKeys(nodes, []);
  const tbtn = { flex: 1, height: 28, borderRadius: 7, border: "1px solid var(--line)", background: "var(--surface)", color: "var(--ink-3)", font: "inherit", fontSize: 11.5, fontWeight: 700, cursor: "pointer", display: "inline-flex", alignItems: "center", justifyContent: "center", gap: 5 };
  return (
    <div>
      {branchKeys.length > 0 && (
        <div style={{ display: "flex", gap: 6, padding: "0 4px 8px" }}>
          <button style={tbtn} onClick={() => setExpanded(new Set(branchKeys))}><Icon.chevD size={12} /> Mở rộng tất cả</button>
          <button style={tbtn} onClick={() => setExpanded(new Set())}><Icon.chevR size={12} /> Thu gọn tất cả</button>
        </div>
      )}
      {nodes.map(n => <VirtualTreeNode key={n.key} node={n} sel={sel} onSelect={onSelect} depth={0} propsById={propsById} expanded={expanded} onToggle={onToggle} visible={visible} onToggleVisible={onToggleVisible} />)}
    </div>
  );
}

/* ---------- helpers ---------- */
// organizable candidates: built-in fields (except folder) + custom groupable props -> {id,label}
function orgCandidates(properties) {
  const builtin = (window.ORG_FIELDS || [])
    .filter(f => f.id !== "folder" && !(window.isSysHidden && window.isSysHidden(f.id)))
    .map(f => ({ id: f.id, label: (window.sysFieldMeta ? window.sysFieldMeta(f.id).name : f.label) }));
  const custom = (window.organizableProps ? window.organizableProps(properties) : []).map(p => ({ id: p.id, label: p.name }));
  return [...builtin, ...custom];
}
// value options for a scope/organize field -> [{v,label,color?}] (colors match the app's chips)
const STATUS_CLS_COL = { idle: "#6b7280", warn: "#f59e0b", str: "#14b8a6", ok: "#10b981", danger: "#ef4444", inf: "#60a5fa" };
function scopeFieldOptions(fieldId, properties) {
  if (fieldId === "disc") return Object.entries(window.DISC_NAME || {}).map(([v, label]) => ({ v, label, color: (window.DISC_COL || {})[v] }));
  if (fieldId === "status") return Object.entries(window.STATUS || {}).map(([v, o]) => ({ v, label: o.label, color: STATUS_CLS_COL[o.cls] }));
  if (fieldId === "type") return Object.keys(window.TYPE_META || {}).map(v => ({ v, label: v, color: (window.TYPE_META[v] || {}).color }));
  const p = (properties || []).find(x => x.id === fieldId);
  if (p && (p.type === "select" || p.type === "status" || p.type === "multi_select")) return (p.options || []).map(o => ({ v: o.id, label: o.label, color: o.color }));
  if (p && p.type === "checkbox") return [{ v: "true", label: "Có", color: "#10b981" }, { v: "false", label: "Không", color: "#6b7280" }];
  return [];
}

/* ---------- custom dropdown (native <select> option list is unstylable) ---------- */
const ddBtn = { width: "100%", height: 38, borderRadius: 10, border: "1px solid var(--line)", background: "var(--sunken)", color: "var(--ink)", font: "inherit", fontSize: 13, fontWeight: 600, padding: "0 12px", cursor: "pointer", display: "flex", alignItems: "center", justifyContent: "space-between", gap: 8, textAlign: "left" };
function Dropdown({ value, options, placeholder, dashed, onChange }) {
  const [open, setOpen] = React.useState(false);
  const ref = React.useRef(null);
  React.useEffect(() => {
    if (!open) return;
    const h = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    document.addEventListener("mousedown", h);
    return () => document.removeEventListener("mousedown", h);
  }, [open]);
  const cur = options.find(o => o.id === value);
  return (
    <div ref={ref} style={{ position: "relative", flex: 1, minWidth: 0, zIndex: open ? 40 : "auto" }}>
      <button type="button" onClick={() => setOpen(o => !o)}
        style={{ ...ddBtn, ...(dashed ? { border: "1.5px dashed var(--line)", background: "transparent", color: "var(--ink-3)" } : {}) }}>
        <span style={{ overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{cur ? cur.label : placeholder}</span>
        <span style={{ flex: "none", display: "grid", placeItems: "center", transform: open ? "rotate(270deg)" : "rotate(90deg)", transition: "transform .15s" }}>
          <Icon.chevR size={13} color="var(--ink-4)" />
        </span>
      </button>
      {open && (
        <div style={{ position: "absolute", top: "calc(100% + 5px)", left: 0, right: 0, zIndex: 41, background: "var(--surface)", border: "1px solid var(--line)", borderRadius: 11, boxShadow: "var(--shadow-lg)", padding: 4, maxHeight: 230, overflowY: "auto" }}>
          {options.map(o => {
            const on = o.id === value;
            return (
              <div key={o.id || "_none"} onClick={() => { setOpen(false); if (o.id !== value) onChange(o.id); }}
                onMouseEnter={e => { e.currentTarget.style.background = "var(--sunken)"; }}
                onMouseLeave={e => { e.currentTarget.style.background = on ? "var(--accent-soft)" : "transparent"; }}
                style={{ display: "flex", alignItems: "center", gap: 8, padding: "8px 11px", borderRadius: 8, cursor: "pointer", fontSize: 13, fontWeight: on ? 700 : 600, color: on ? "var(--accent)" : "var(--ink-2)", background: on ? "var(--accent-soft)" : "transparent" }}>
                <span style={{ width: 14, flex: "none", display: "grid", placeItems: "center" }}>{on ? <Icon.check size={12} color="var(--accent)" /> : null}</span>
                <span style={{ overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{o.label}</span>
              </div>
            );
          })}
        </div>
      )}
    </div>
  );
}

/* ---------- config panel ---------- */
function OrgConfigPanel({ properties, config, onApply, onClose }) {
  const candidates = orgCandidates(properties);
  const candIds = new Set(candidates.map(c => c.id));
  // self-heal: ignore levels whose property was deleted/hidden so the panel shows A→C, not a dangling B.
  // No forced default org — start from the user's saved levels (empty = plain folder view).
  const personal = (config.organizeBy || []).filter(k => k !== "folder" && candIds.has(k));
  const [picked, setPicked] = React.useState(personal);
  const [hideEmpty, setHideEmpty] = React.useState(!!config.hideEmpty);
  const scope0 = config.scope || {};
  const scope0key = Object.keys(scope0)[0] || "";
  const [scopeField, setScopeField] = React.useState(scope0key);
  const [scopeVals, setScopeVals] = React.useState(scope0key ? (scope0[scope0key] || []) : []);
  // saved organize views (named, persisted to the user's account)
  const [saved, setSaved] = React.useState([]);
  React.useEffect(() => { if (window.cdeData && cdeData.orgViews) cdeData.orgViews().then(setSaved).catch(() => setSaved([])); }, []);

  // one combobox per level; picking in the trailing empty box adds a level, clearing a box removes it
  const setLevel = (i, v) => setPicked(a => {
    const n = a.slice();
    if (!v) { if (i < n.length) n.splice(i, 1); return n; }
    if (i < n.length) n[i] = v; else n.push(v);
    return n;
  });
  const removeLevel = (i) => setPicked(a => a.filter((_, j) => j !== i));
  const moveLevel = (from, to) => {
    if (from == null || to == null || from === to) return;
    setPicked(a => { const n = [...a]; const [x] = n.splice(from, 1); n.splice(to, 0, x); return n; });
  };
  const dragFrom = React.useRef(null);
  const [dropIdx, setDropIdx] = React.useState(null);
  const toggleVal = (v) => setScopeVals(a => a.includes(v) ? a.filter(x => x !== v) : [...a, v]);
  const scopeOpts = scopeField ? scopeFieldOptions(scopeField, properties) : [];
  const remaining = candidates.filter(c => !picked.includes(c.id)).map(c => ({ id: c.id, label: c.label }));

  const curConfig = () => {
    const scope = (scopeField && scopeVals.length) ? { [scopeField]: scopeVals } : {};
    return { organizeBy: picked.length ? picked : ["status", "disc"], scope, hideEmpty };
  };
  const apply = () => onApply(curConfig());
  const saveCurrent = async () => {
    if (!(picked.length || (scopeField && scopeVals.length))) { window.alert("Chưa có cấp tổ chức nào để lưu."); return; }
    const name = window.prompt("Đặt tên cho cách tổ chức này:", "");
    if (!name || !name.trim()) return;
    try { const v = await cdeData.saveOrgView(name.trim(), curConfig()); setSaved(s => [...s, v]); }
    catch (e) { window.alert(e.message || "Lưu thất bại"); }
  };
  const delView = async (v) => {
    if (!window.confirm(`Xoá cách tổ chức "${v.name}"?`)) return;
    try { await cdeData.deleteOrgView(v.id); setSaved(s => s.filter(x => x.id !== v.id)); }
    catch (e) { window.alert(e.message); }
  };
  const lbl = { display: "block", fontSize: 11, fontWeight: 800, letterSpacing: ".07em", color: "var(--ink-4)", margin: "0 0 10px" };
  const divider = <div style={{ height: 1, background: "var(--line)", margin: "18px -22px" }} />;
  const elbow = <span style={{ width: 11, height: 18, marginTop: -12, flex: "none", borderLeft: "2px solid var(--line)", borderBottom: "2px solid var(--line)", borderBottomLeftRadius: 7 }} />;

  return (
    <div onClick={onClose} style={{ position: "fixed", inset: 0, zIndex: 55, background: "rgba(8,19,23,.5)", backdropFilter: "blur(3px)", display: "grid", placeItems: "center" }}>
      <div onClick={e => e.stopPropagation()} className="card" style={{ width: "min(480px, 94vw)", maxHeight: "90vh", overflowY: "auto", padding: 22, boxShadow: "var(--shadow-lg)" }}>
        <div style={{ display: "flex", alignItems: "center", gap: 12 }}>
          <span style={{ width: 38, height: 38, borderRadius: 11, background: "var(--accent-soft)", color: "var(--accent)", display: "grid", placeItems: "center", flex: "none" }}><Icon.layers size={19} /></span>
          <div>
            <div style={{ fontWeight: 800, fontSize: 16 }}>Cây thư mục dự án</div>
            <div style={{ fontSize: 12, color: "var(--ink-3)", fontWeight: 500, marginTop: 1 }}>Chỉ thay đổi cách xem của riêng bạn</div>
          </div>
        </div>
        {divider}

        <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 10 }}>
          <label style={{ ...lbl, margin: 0 }}>BỘ TỔ CHỨC ĐÃ LƯU</label>
          <button className="btn sm" style={{ flex: "none" }} onClick={saveCurrent}><Icon.plus size={12} /> Lưu cách hiện tại</button>
        </div>
        {saved.length === 0
          ? <div style={{ fontSize: 12, color: "var(--ink-4)", margin: "-2px 0 6px" }}>Chưa lưu cách nào — bấm "Lưu cách hiện tại" để lưu vào tài khoản.</div>
          : saved.map(v => (
            <div key={v.id} onClick={() => onApply(v.config)}
              onMouseEnter={e => { e.currentTarget.style.background = "var(--sunken)"; }}
              onMouseLeave={e => { e.currentTarget.style.background = "transparent"; }}
              style={{ display: "flex", alignItems: "center", gap: 9, padding: "8px 10px", margin: "0 -10px 2px", borderRadius: 9, cursor: "pointer" }}>
              <span style={{ width: 26, height: 26, borderRadius: 7, flex: "none", display: "grid", placeItems: "center", background: "var(--accent-soft)", color: "var(--accent)" }}><Icon.layers size={14} /></span>
              <span style={{ flex: 1, minWidth: 0, fontSize: 13, fontWeight: 700, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{v.name}</span>
              <button className="icon-btn" style={{ width: 26, height: 26, flex: "none" }} title="Xoá" onClick={e => { e.stopPropagation(); delView(v); }}><Icon.trash size={12} /></button>
            </div>
          ))}
        {divider}

        <label style={lbl}>CẤP THƯ MỤC · TO → BÉ</label>
        {picked.length === 0 && <div style={{ fontSize: 12, color: "var(--ink-4)", margin: "-2px 0 8px" }}>Chưa chọn cấp nào — đang xem theo thư mục thật.</div>}
        {picked.map((id, i) => {
          const used = picked.filter((x, j) => j !== i);
          const opts = candidates.filter(c => c.id === id || !used.includes(c.id)).map(c => ({ id: c.id, label: c.label }));
          return (
            <div key={id} draggable
              onDragStart={e => { dragFrom.current = i; e.dataTransfer.effectAllowed = "move"; }}
              onDragOver={e => { e.preventDefault(); if (dropIdx !== i) setDropIdx(i); }}
              onDragLeave={() => setDropIdx(d => (d === i ? null : d))}
              onDrop={e => { e.preventDefault(); setDropIdx(null); moveLevel(dragFrom.current, i); dragFrom.current = null; }}
              onDragEnd={() => setDropIdx(null)}
              style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 8, marginLeft: i * 20, borderRadius: 11, outline: dropIdx === i ? "2px dashed var(--accent)" : "none", outlineOffset: 2 }}>
              {i > 0 && elbow}
              <span title="Kéo thả để đổi thứ tự" style={{ width: 14, flex: "none", textAlign: "center", cursor: "grab", color: "var(--ink-4)", fontSize: 13, userSelect: "none" }}>⠿</span>
              <span style={{ width: 22, height: 22, borderRadius: 7, background: "var(--accent)", color: "#fff", fontSize: 11.5, fontWeight: 800, display: "grid", placeItems: "center", flex: "none" }}>{i + 1}</span>
              <Dropdown value={id} options={opts} onChange={v => setLevel(i, v)} />
              <button className="icon-btn" title="Xóa cấp này" onClick={() => removeLevel(i)} style={{ width: 28, height: 28, flex: "none" }}>✕</button>
            </div>
          );
        })}
        {remaining.length > 0 && (
          <div style={{ display: "flex", alignItems: "center", gap: 8, marginLeft: picked.length * 20 }}>
            {picked.length > 0 && elbow}
            <span style={{ width: 14, flex: "none" }} />
            <span style={{ width: 22, height: 22, borderRadius: 7, background: "var(--sunken)", color: "var(--ink-4)", fontSize: 11.5, fontWeight: 800, display: "grid", placeItems: "center", flex: "none" }}>{picked.length + 1}</span>
            <Dropdown dashed value="" placeholder="+ Thêm cấp con" options={remaining} onChange={v => setLevel(picked.length, v)} />
            <span style={{ width: 28, flex: "none" }} />
          </div>
        )}

        <div onClick={() => setHideEmpty(v => !v)} style={{ display: "flex", alignItems: "center", gap: 11, marginTop: 12, padding: "10px 12px", borderRadius: 11, background: "var(--sunken)", cursor: "pointer", userSelect: "none" }}>
          <span style={{ width: 34, height: 20, borderRadius: 99, flex: "none", position: "relative", background: hideEmpty ? "var(--accent)" : "var(--line)", transition: "background .15s" }}>
            <span style={{ position: "absolute", top: 2, left: hideEmpty ? 16 : 2, width: 16, height: 16, borderRadius: "50%", background: "#fff", boxShadow: "0 1px 3px rgba(0,0,0,.3)", transition: "left .15s" }} />
          </span>
          <div style={{ minWidth: 0 }}>
            <div style={{ fontSize: 13, fontWeight: 700 }}>Ẩn thư mục trống</div>
            <div style={{ fontSize: 11.5, color: "var(--ink-4)", fontWeight: 500 }}>Chỉ hiện nhánh có hồ sơ bên trong</div>
          </div>
        </div>
        {divider}

        <label style={lbl}>VÙNG CỦA TÔI · LỌC CÁ NHÂN</label>
        <div style={{ display: "flex", alignItems: "center", gap: 9 }}>
          <span style={{ width: 22, height: 22, borderRadius: 7, background: scopeField ? "var(--accent-soft)" : "var(--sunken)", color: scopeField ? "var(--accent)" : "var(--ink-4)", display: "grid", placeItems: "center", flex: "none" }}><Icon.filter size={12} /></span>
          <Dropdown dashed={!scopeField} value={scopeField} placeholder="— Không lọc —"
            options={[{ id: "", label: "— Không lọc —" }, ...candidates.map(c => ({ id: c.id, label: c.label }))]}
            onChange={v => { setScopeField(v); setScopeVals([]); }} />
        </div>
        {scopeField && (
          <div style={{ display: "flex", flexWrap: "wrap", gap: 6, marginTop: 10, marginLeft: 31 }}>
            {scopeOpts.map(o => {
              const on = scopeVals.includes(o.v);
              return <span key={o.v} onClick={() => toggleVal(o.v)} style={{ cursor: "pointer", height: 28, padding: "0 12px", borderRadius: 99, fontSize: 12, fontWeight: 700, display: "inline-flex", alignItems: "center", gap: 5, color: on ? "#fff" : "var(--ink-3)", background: on ? (o.color || "var(--accent)") : "var(--sunken)", border: on ? "1px solid transparent" : "1px solid var(--line)", transition: "all .12s" }}>{on && <Icon.check size={11} color="#fff" />}{o.label}</span>;
            })}
          </div>
        )}
        {divider}

        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", gap: 10 }}>
          <button className="btn" onClick={() => onApply({ organizeBy: ["status", "disc"], scope: {}, hideEmpty: false })}>Mặc định (Trạng thái → Bộ môn)</button>
          <div style={{ display: "flex", gap: 10 }}>
            <button className="btn" onClick={onClose}>Đóng</button>
            <button className="btn primary" onClick={apply}><Icon.check size={14} /> Áp dụng</button>
          </div>
        </div>
      </div>
    </div>
  );
}

window.VirtualTree = VirtualTree;
window.OrgConfigPanel = OrgConfigPanel;
window.scopeFieldOptions = scopeFieldOptions;
