
/* ==========================================================================
 * ADMIN NEXT — page-admins.jsx  (จัดการผู้ดูแลระบบ + Preset สิทธิ์)
 * ========================================================================== */

/* ใช้เมื่อโหลดรายชื่อสิทธิ์ภาษาไทยจาก /permissions/me ไม่ได้ */
const PERM_GROUPS = [
  ["ภาพรวม",["home","summary"]],
  ["สมาชิก",["users","tool_member_levels","tool_fraud_list","tool_manage_credit","tool_top_depositors"]],
  ["รายการเงิน",["deposits","wdr","tool_slip_approve","tool_suspicious_deposit","tool_sms_history","payment"]],
  ["ธนาคาร",["bank-receive","bank-payout","bank-central","tool_payment_manager"]],
  ["เกม",["game-manager","game-sort","provider-logos","tool_featured_providers","tool_featured_games","games"]],
  ["การตลาด",["promo","codes","tool_checkin","tool_cashback","tool_wheel","tool_affiliate","prediction","tool_mkt_report"]],
  ["รายงาน",["tool_report_pl","tool_report_promo","tool_ranking","tool_provider_reports","tool_staff_kpi"]],
  ["ประวัติ",["tool_history_all","tool_history_wheel","tool_history_referral","tool_history_cashback","tool_history_bonus","tool_manual_report","tool_otp_log","tool_promo_requests","tool_history_prediction"]],
  ["ตั้งค่า",["settings","tool_domains","tool_notifications","tool_popup_contact","perm_edit_permissions","logs"]],
  ["API",["settings_agent","settings_deposit_special","tool_sms_gateway","tool_server_health","tool_white_label"]],
];
const PERM_GROUP_ORDER = ["ภาพรวม","ผู้ใช้งาน","รายการเงิน","ธนาคาร","เกม","การตลาด","ประวัติต่างๆ","ตั้งค่า","สิทธิ์พิเศษ","จัดการ API","จัดการ API (พิเศษ)"];
const ROLE_BADGE = { admin:"accent", staff:"info" };
const STATUS_BADGE = { active:"success", suspended:"danger" };

let PERM_CATALOG_PROMISE = null;
function loadPermCatalog() {
  if (!PERM_CATALOG_PROMISE) {
    PERM_CATALOG_PROMISE = api("/permissions/me")
      .then(me => (Array.isArray(me && me.pages) && me.pages.length) ? me.pages : null)
      .catch(() => null)
      .then(pages => pages || PERM_GROUPS.flatMap(([group, keys]) => keys.map(key => ({ key, label: key, group }))));
  }
  return PERM_CATALOG_PROMISE;
}

function groupPermCatalog(pages) {
  const byGroup = new Map();
  pages.forEach(p => {
    const name = p.group || "อื่นๆ";
    if (!byGroup.has(name)) byGroup.set(name, []);
    byGroup.get(name).push({ key: p.key, label: p.label || p.key });
  });
  const rank = (name) => { const i = PERM_GROUP_ORDER.indexOf(name); return i < 0 ? PERM_GROUP_ORDER.length : i; };
  return [...byGroup.entries()].sort((a, b) => rank(a[0]) - rank(b[0])).map(([name, items]) => ({ name, items }));
}

function usePermCatalog() {
  const [groups, setGroups] = useState(null);
  useEffect(() => { let live = true; loadPermCatalog().then(pages => { if (live) setGroups(groupPermCatalog(pages)); }); return () => { live = false; }; }, []);
  const labelOf = useMemo(() => {
    const m = new Map(); (groups || []).forEach(g => g.items.forEach(it => m.set(it.key, it.label)));
    return (key) => m.get(key) || key;
  }, [groups]);
  return { groups, labelOf };
}

function useRolePresets(toast) {
  const [presets, setPresets] = useState([]);
  const [loading, setLoading] = useState(true);
  const reload = useCallback(async () => {
    setLoading(true);
    try { const list = await api("/role-presets"); setPresets(Array.isArray(list) ? list : []); }
    catch (e) { if (e.status === 401) window.__nxAuthFail && window.__nxAuthFail(); setPresets([]); }
    finally { setLoading(false); }
  }, []);
  useEffect(() => { if (can("perm_edit_permissions")) reload(); else setLoading(false); }, [reload]);
  const saveAll = useCallback(async (next) => {
    const saved = await api("/role-presets", { method: "POST", body: next });
    setPresets(Array.isArray(saved && saved.presets) ? saved.presets : next);
    toast && toast("บันทึก Preset แล้ว");
  }, [toast]);
  return { presets, loading, reload, saveAll };
}

function AdminModalShell({ title, sub, onClose, busy, maxW = "max-w-2xl", children, footer }) {
  return (<div className="fixed inset-0 z-[80] flex items-center justify-center p-4 fade-in" onMouseDown={() => !busy && onClose()}>
    <div className="absolute inset-0 bg-black/55"/>
    <div className={"pop relative flex max-h-[92vh] w-full flex-col rounded-2xl border border-line bg-surface2 shadow-lg " + maxW} onMouseDown={e => e.stopPropagation()}>
      <div className="flex items-start justify-between gap-3 border-b border-line-soft px-5 pb-3 pt-4">
        <div className="min-w-0"><div className="truncate text-base font-semibold">{title}</div>{sub && <p className="mt-0.5 text-xs text-muted">{sub}</p>}</div>
        <button onClick={() => !busy && onClose()} className="grid h-8 w-8 shrink-0 place-items-center rounded-lg text-fg-2 hover:bg-sunken"><Ic d={I.x} size={16}/></button>
      </div>
      <div className="min-h-0 flex-1 overflow-y-auto px-5 py-4">{children}</div>
      {footer && <div className="border-t border-line-soft px-5 py-3">{footer}</div>}
    </div>
  </div>);
}

/* ตารางเลือกสิทธิ์ — ชื่อเมนูภาษาไทย ค้นหาได้ สิทธิ์ที่ตัวเองไม่มีจะให้คนอื่นไม่ได้ (backend ก็ตัดทิ้ง) */
function PermPicker({ groups, selected, onChange }) {
  const [q, setQ] = useState("");
  if (!groups) return (<div className="space-y-3">{Array.from({ length: 4 }).map((_, i) => <div key={i} className="skel h-14 w-full"/>)}</div>);
  const term = q.trim().toLowerCase();
  const shown = groups
    .map(g => ({ ...g, items: term ? g.items.filter(it => it.label.toLowerCase().includes(term) || it.key.toLowerCase().includes(term)) : g.items }))
    .filter(g => g.items.length);
  const setKeys = (keys, on) => { const n = new Set(selected); keys.forEach(k => on ? n.add(k) : n.delete(k)); onChange(n); };
  return (<div>
    <div className="relative mb-3">
      <span className="pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-faint"><Ic d={I.search} size={15}/></span>
      <input value={q} onChange={e => setQ(e.target.value)} placeholder="ค้นหาเมนู เช่น ถอน, โปร, รายงาน" className="h-9 w-full rounded-lg border border-line bg-sunken pl-9 pr-3 text-sm text-fg placeholder:text-faint focus:border-accent focus:bg-surface focus:outline-none focus:ring-4 focus:ring-[var(--accent-ring)]"/>
    </div>
    {shown.length === 0 && <div className="py-8 text-center text-sm text-muted">ไม่พบเมนูที่ค้นหา</div>}
    <div className="space-y-4">{shown.map(g => {
      const keys = g.items.map(it => it.key).filter(k => can(k));
      const onCount = g.items.filter(it => selected.has(it.key)).length;
      const all = keys.length > 0 && keys.every(k => selected.has(k));
      return (<div key={g.name}>
        <button type="button" disabled={!keys.length} onClick={() => setKeys(keys, !all)} className="mb-1.5 flex items-center gap-2 text-xs font-semibold text-fg-2 transition-colors hover:text-fg disabled:opacity-50">
          <input type="checkbox" readOnly checked={all} className="h-3.5 w-3.5 accent-[var(--accent)]"/>
          {g.name}<span className="font-normal text-muted tnum">{onCount}/{g.items.length}</span>
        </button>
        <div className="flex flex-wrap gap-2">{g.items.map(it => {
          const on = selected.has(it.key); const locked = !can(it.key);
          return (<button type="button" key={it.key} title={locked ? it.key + " — คุณไม่มีสิทธิ์นี้ จึงให้คนอื่นไม่ได้" : it.key} disabled={locked}
            onClick={() => setKeys([it.key], !on)}
            className={"flex items-center gap-1.5 rounded-lg border px-2.5 py-1.5 text-xs transition-colors duration-150 disabled:cursor-not-allowed disabled:opacity-45 " + (on ? "border-accent/40 bg-accent-soft font-medium text-accent" : "border-line bg-sunken text-fg-2 hover:bg-surface")}>
            {on && <Ic d={I.check} size={12} sw={2.4}/>}{it.label}
          </button>);
        })}</div>
      </div>);
    })}</div>
  </div>);
}

/* เลือก Preset แล้วกด "ใช้ Preset" — แทนที่สิทธิ์ที่เลือกอยู่ทั้งหมด */
function PresetPickBar({ presets, onApply }) {
  const [name, setName] = useState("");
  if (!presets.length) return (<div className="mb-4 rounded-lg border border-dashed border-line px-3 py-2.5 text-xs text-muted">ยังไม่มี Preset — สร้างได้ที่ปุ่ม "Preset สิทธิ์" หน้ารายชื่อแอดมิน หรือกด "บันทึกเป็น Preset" ด้านล่าง</div>);
  const apply = () => { const p = presets.find(x => x.name === name); if (p) onApply(p); };
  return (<div className="mb-4 flex items-center gap-2 rounded-lg border border-accent/25 bg-accent-soft px-3 py-2">
    <span className="shrink-0 text-xs font-semibold text-accent">Preset</span>
    <select value={name} onChange={e => setName(e.target.value)} className="h-8 min-w-0 flex-1 rounded-lg border border-line bg-surface px-2 text-sm text-fg focus:border-accent focus:outline-none">
      <option value="">— เลือก Preset —</option>
      {presets.map(p => <option key={p.name} value={p.name}>{p.name} ({(p.permissions || []).length} สิทธิ์)</option>)}
    </select>
    <button type="button" disabled={!name} onClick={apply} className="h-8 shrink-0 rounded-lg bg-accent px-3 text-xs font-semibold text-accent-fg transition-colors hover:bg-accent-h disabled:opacity-50">ใช้ Preset</button>
  </div>);
}

function grantablePresetPerms(preset) { return new Set((preset.permissions || []).filter(k => can(k))); }

function PermModal({ admin, catalog, presetsApi, onClose, onDone, toast }) {
  const [perms, setPerms] = useState(new Set(admin.permissions || []));
  const [busy, setBusy] = useState(false);
  const [saveAs, setSaveAs] = useState(null);
  const applyPreset = (p) => { setPerms(grantablePresetPerms(p)); toast("โหลด Preset \"" + p.name + "\" แล้ว — กดบันทึกสิทธิ์เพื่อยืนยัน"); };
  const allGrantable = () => (catalog.groups || []).flatMap(g => g.items.map(it => it.key)).filter(k => can(k));
  const save = async () => {
    setBusy(true);
    try { await api("/admins/" + admin.id + "/permissions", { method: "POST", body: { permissions: [...perms] } }); toast("บันทึกสิทธิ์สำเร็จ"); onDone && onDone(); onClose(); }
    catch (e) { if (e.status === 401 || e.status === 403) { window.__nxAuthFail && window.__nxAuthFail(); } toast(e.message || "ไม่สำเร็จ"); }
    finally { setBusy(false); }
  };
  const savePreset = async () => {
    const name = (saveAs || "").trim().slice(0, 40);
    if (!name) { toast("ตั้งชื่อ Preset ก่อน"); return; }
    if (!perms.size) { toast("เลือกสิทธิ์อย่างน้อย 1 อย่าง"); return; }
    const exists = presetsApi.presets.some(p => p.name === name);
    if (exists && !window.confirm("มี Preset ชื่อ \"" + name + "\" แล้ว ต้องการเขียนทับ?")) return;
    setBusy(true);
    try { await presetsApi.saveAll([...presetsApi.presets.filter(p => p.name !== name), { name, permissions: [...perms] }]); setSaveAs(null); }
    catch (e) { toast(e.message || "บันทึก Preset ไม่สำเร็จ"); }
    finally { setBusy(false); }
  };
  const footer = (<div className="flex flex-wrap items-center justify-between gap-2">
    {saveAs === null ? (<div className="flex items-center gap-3 text-xs">
      <button type="button" onClick={() => setPerms(new Set(allGrantable()))} className="text-accent hover:underline">เลือกทั้งหมด</button>
      <button type="button" onClick={() => setPerms(new Set())} className="text-fg-2 hover:underline">ล้าง</button>
      <button type="button" onClick={() => setSaveAs("")} className="text-accent hover:underline">บันทึกเป็น Preset</button>
    </div>) : (<div className="flex min-w-0 flex-1 items-center gap-2">
      <input autoFocus value={saveAs} onChange={e => setSaveAs(e.target.value)} onKeyDown={e => e.key === "Enter" && savePreset()} placeholder="ชื่อ Preset เช่น ฝ่ายการเงิน" className="h-9 min-w-0 flex-1 rounded-lg border border-line bg-sunken px-3 text-sm text-fg focus:border-accent focus:outline-none sm:max-w-[220px]"/>
      <button disabled={busy} onClick={savePreset} className="h-9 shrink-0 rounded-lg border border-accent/40 px-3 text-xs font-semibold text-accent hover:bg-accent-soft disabled:opacity-50">บันทึก Preset</button>
      <button onClick={() => setSaveAs(null)} className="h-9 shrink-0 px-1 text-xs text-fg-2 hover:underline">ยกเลิก</button>
    </div>)}
    <div className="flex gap-2">
      <button onClick={onClose} className="h-9 rounded-lg border border-line bg-surface px-3 text-sm font-medium text-fg-2 hover:bg-sunken">ยกเลิก</button>
      <button disabled={busy} onClick={save} className="h-9 rounded-lg bg-accent px-4 text-sm font-semibold text-accent-fg hover:bg-accent-h disabled:opacity-50">{busy ? "กำลังทำ…" : "บันทึกสิทธิ์ (" + perms.size + ")"}</button>
    </div>
  </div>);
  const lockedHeld = [...perms].filter(k => !can(k));
  return (<AdminModalShell title={"แก้ไขสิทธิ์ — " + admin.username} sub={(admin.full_name || admin.username) + " · " + admin.role} onClose={onClose} busy={busy} footer={footer}>
    {lockedHeld.length > 0 && <div className="mb-3 rounded-lg border px-3 py-2 text-xs" style={{ borderColor: "var(--warn)", background: "var(--warn-soft)", color: "var(--warn)" }}>
      คนนี้มีสิทธิ์ที่คุณไม่มี {lockedHeld.length} รายการ ({lockedHeld.map(catalog.labelOf).join(", ")}) — ถ้ากดบันทึก ระบบจะถอดสิทธิ์เหล่านี้ออก ให้เจ้าของเว็บเป็นคนแก้แทนถ้าไม่ต้องการ
    </div>}
    <PresetPickBar presets={presetsApi.presets} onApply={applyPreset}/>
    <PermPicker groups={catalog.groups} selected={perms} onChange={setPerms}/>
  </AdminModalShell>);
}

function AdminPasswordModal({ admin, onClose, toast }) {
  const [pw, setPw] = useState("");
  const [pw2, setPw2] = useState("");
  const [busy, setBusy] = useState(false);
  const inp = "mt-1 h-10 w-full rounded-lg border border-line bg-sunken px-3 text-sm text-fg placeholder:text-faint focus:border-accent focus:bg-surface focus:outline-none focus:ring-4 focus:ring-[var(--accent-ring)]";
  const submit = async () => {
    if (pw.length < 8) { toast("รหัสผ่านต้องมีอย่างน้อย 8 ตัวอักษร"); return; }
    if (pw !== pw2) { toast("รหัสผ่านทั้งสองช่องไม่ตรงกัน"); return; }
    setBusy(true);
    try { await api("/admins/" + admin.id + "/password", { method: "POST", body: { password: pw } }); toast("เปลี่ยนรหัสผ่านของ " + admin.username + " แล้ว"); onClose(); }
    catch (e) { if (e.status === 401) { window.__nxAuthFail && window.__nxAuthFail(); } toast(e.message || "ไม่สำเร็จ"); }
    finally { setBusy(false); }
  };
  const footer = (<div className="flex items-center justify-end gap-2">
    <button onClick={onClose} className="h-9 rounded-lg border border-line bg-surface px-3 text-sm font-medium text-fg-2 hover:bg-sunken">ยกเลิก</button>
    <button disabled={busy} onClick={submit} className="h-9 rounded-lg bg-accent px-4 text-sm font-semibold text-accent-fg hover:bg-accent-h disabled:opacity-50">{busy ? "กำลังทำ…" : "เปลี่ยนรหัสผ่าน"}</button>
  </div>);
  return (<AdminModalShell title={"เปลี่ยนรหัสผ่าน — " + admin.username} sub={(admin.full_name || admin.username) + " · " + admin.role} onClose={onClose} busy={busy} maxW="max-w-sm" footer={footer}>
    <div className="space-y-3">
      <label className="block"><span className="text-xs font-medium text-fg-2">รหัสผ่านใหม่ (8 ตัวขึ้นไป)</span><input type="password" autoComplete="new-password" autoFocus className={inp} value={pw} onChange={e => setPw(e.target.value)}/></label>
      <label className="block"><span className="text-xs font-medium text-fg-2">ยืนยันรหัสผ่านใหม่</span><input type="password" autoComplete="new-password" className={inp} value={pw2} onChange={e => setPw2(e.target.value)} onKeyDown={e => e.key === "Enter" && submit()}/></label>
      <p className="text-xs text-muted">ยูสนี้ต้องใช้รหัสผ่านใหม่ในการเข้าสู่ระบบครั้งถัดไป</p>
    </div>
  </AdminModalShell>);
}

function CreateAdminModal({ presets, onClose, onDone, toast }) {
  const [f, setF] = useState({ username: "", password: "", fullName: "", role: "admin", preset: "" });
  const [busy, setBusy] = useState(false);
  const set = (k, v) => setF(o => ({ ...o, [k]: v }));
  const inp = "mt-1 h-10 w-full rounded-lg border border-line bg-sunken px-3 text-sm text-fg placeholder:text-faint focus:border-accent focus:bg-surface focus:outline-none focus:ring-4 focus:ring-[var(--accent-ring)]";
  const chosen = presets.find(p => p.name === f.preset);
  const submit = async () => {
    if (!f.username.trim() || !f.password) { toast("กรอก Username และ Password"); return; }
    if (f.password.length < 8) { toast("รหัสผ่านต้องมีอย่างน้อย 8 ตัวอักษร"); return; }
    setBusy(true);
    try {
      const permissions = chosen ? [...grantablePresetPerms(chosen)] : [];
      await api("/admins", { method: "POST", body: { username: f.username.trim(), password: f.password, fullName: f.fullName.trim() || f.username.trim(), role: f.role, permissions } });
      toast(chosen ? "สร้างแอดมินพร้อม Preset \"" + chosen.name + "\" แล้ว" : "สร้างแอดมินสำเร็จ"); onDone && onDone(); onClose();
    }
    catch (e) { if (e.status === 401 || e.status === 403) { window.__nxAuthFail && window.__nxAuthFail(); } toast(e.message || "ไม่สำเร็จ"); }
    finally { setBusy(false); }
  };
  const footer = (<div className="flex items-center justify-end gap-2">
    <button onClick={onClose} className="h-9 rounded-lg border border-line bg-surface px-3 text-sm font-medium text-fg-2 hover:bg-sunken">ยกเลิก</button>
    <button disabled={busy} onClick={submit} className="h-9 rounded-lg bg-accent px-4 text-sm font-semibold text-accent-fg hover:bg-accent-h disabled:opacity-50">{busy ? "กำลังทำ…" : "สร้าง"}</button>
  </div>);
  return (<AdminModalShell title="เพิ่มแอดมิน" onClose={onClose} busy={busy} maxW="max-w-sm" footer={footer}>
    <div className="space-y-3">
      <label className="block"><span className="text-xs font-medium text-fg-2">Username *</span><input className={inp} value={f.username} onChange={e => set("username", e.target.value)} placeholder="username"/></label>
      <label className="block"><span className="text-xs font-medium text-fg-2">Password * (8 ตัวขึ้นไป)</span><input type="password" className={inp} value={f.password} onChange={e => set("password", e.target.value)} placeholder="password"/></label>
      <label className="block"><span className="text-xs font-medium text-fg-2">ชื่อแสดง</span><input className={inp} value={f.fullName} onChange={e => set("fullName", e.target.value)} placeholder="ชื่อ-นามสกุล"/></label>
      <label className="block"><span className="text-xs font-medium text-fg-2">Role</span>
        <select className={inp} value={f.role} onChange={e => set("role", e.target.value)}><option value="admin">admin</option><option value="staff">staff</option></select></label>
      <label className="block"><span className="text-xs font-medium text-fg-2">Preset สิทธิ์</span>
        <select className={inp} value={f.preset} onChange={e => set("preset", e.target.value)}>
          <option value="">— ไม่ใช้ (ตั้งสิทธิ์ทีหลัง) —</option>
          {presets.map(p => <option key={p.name} value={p.name}>{p.name} ({(p.permissions || []).length} สิทธิ์)</option>)}
        </select></label>
    </div>
  </AdminModalShell>);
}

function PresetEditModal({ preset, catalog, presetsApi, onClose, toast }) {
  const [name, setName] = useState(preset ? preset.name : "");
  const [perms, setPerms] = useState(new Set(preset ? preset.permissions || [] : []));
  const [busy, setBusy] = useState(false);
  const save = async () => {
    const clean = name.trim().slice(0, 40);
    if (!clean) { toast("ตั้งชื่อ Preset ก่อน"); return; }
    if (!perms.size) { toast("เลือกสิทธิ์อย่างน้อย 1 อย่าง"); return; }
    const clash = clean !== (preset && preset.name) && presetsApi.presets.some(p => p.name === clean);
    if (clash && !window.confirm("มี Preset ชื่อ \"" + clean + "\" แล้ว ต้องการเขียนทับ?")) return;
    setBusy(true);
    try {
      const rest = presetsApi.presets.filter(p => p.name !== clean && p.name !== (preset && preset.name));
      await presetsApi.saveAll([...rest, { name: clean, permissions: [...perms] }]); onClose();
    }
    catch (e) { toast(e.message || "บันทึกไม่สำเร็จ"); }
    finally { setBusy(false); }
  };
  const footer = (<div className="flex flex-wrap items-center justify-between gap-2">
    <button type="button" onClick={() => setPerms(new Set())} className="text-xs text-fg-2 hover:underline">ล้างที่เลือก</button>
    <div className="flex gap-2">
      <button onClick={onClose} className="h-9 rounded-lg border border-line bg-surface px-3 text-sm font-medium text-fg-2 hover:bg-sunken">ยกเลิก</button>
      <button disabled={busy} onClick={save} className="h-9 rounded-lg bg-accent px-4 text-sm font-semibold text-accent-fg hover:bg-accent-h disabled:opacity-50">{busy ? "กำลังทำ…" : "บันทึก Preset (" + perms.size + ")"}</button>
    </div>
  </div>);
  return (<AdminModalShell title={preset ? "แก้ไข Preset — " + preset.name : "สร้าง Preset สิทธิ์"} sub="แก้ Preset ไม่เปลี่ยนสิทธิ์ของแอดมินที่ตั้งไปแล้ว — กด “ใช้กับแอดมิน” เพื่ออัปเดตหลายคนพร้อมกัน" onClose={onClose} busy={busy} footer={footer}>
    <label className="mb-4 block"><span className="text-xs font-medium text-fg-2">ชื่อ Preset</span>
      <input value={name} onChange={e => setName(e.target.value)} placeholder="เช่น ฝ่ายการเงิน, CS กะดึก" className="mt-1 h-10 w-full rounded-lg border border-line bg-sunken px-3 text-sm text-fg placeholder:text-faint focus:border-accent focus:bg-surface focus:outline-none focus:ring-4 focus:ring-[var(--accent-ring)]"/></label>
    <PermPicker groups={catalog.groups} selected={perms} onChange={setPerms}/>
  </AdminModalShell>);
}

/* ใช้ Preset กับแอดมินหลายคนในครั้งเดียว — แทนที่สิทธิ์เดิมของแต่ละคน */
function PresetApplyModal({ preset, admins, onClose, onDone, toast }) {
  const targets = admins.filter(a => !a.is_owner && a.role !== "admin_dev");
  const [picked, setPicked] = useState(new Set());
  const [busy, setBusy] = useState(false);
  const [progress, setProgress] = useState(0);
  const toggle = (id) => setPicked(s => { const n = new Set(s); n.has(id) ? n.delete(id) : n.add(id); return n; });
  const allOn = targets.length > 0 && targets.every(a => picked.has(a.id));
  const run = async () => {
    const chosen = targets.filter(a => picked.has(a.id));
    if (!chosen.length) { toast("เลือกแอดมินอย่างน้อย 1 คน"); return; }
    if (!window.confirm("ตั้งสิทธิ์ของ " + chosen.length + " คน ให้เท่ากับ Preset \"" + preset.name + "\"? (สิทธิ์เดิมจะถูกแทนที่)")) return;
    setBusy(true);
    const permissions = [...grantablePresetPerms(preset)];
    const failed = [];
    for (let i = 0; i < chosen.length; i++) {
      setProgress(i + 1);
      try { await api("/admins/" + chosen[i].id + "/permissions", { method: "POST", body: { permissions } }); }
      catch (e) { failed.push(chosen[i].username + " (" + (e.message || e.status) + ")"); }
    }
    setBusy(false); setProgress(0);
    toast(failed.length ? "สำเร็จ " + (chosen.length - failed.length) + " คน, ไม่สำเร็จ: " + failed.join(", ") : "ใช้ Preset กับ " + chosen.length + " คนแล้ว");
    onDone && onDone();
    if (!failed.length) onClose();
  };
  const footer = (<div className="flex items-center justify-between gap-2">
    <button type="button" disabled={!targets.length || busy} onClick={() => setPicked(allOn ? new Set() : new Set(targets.map(a => a.id)))} className="text-xs text-accent hover:underline disabled:opacity-50">{allOn ? "ไม่เลือกทั้งหมด" : "เลือกทั้งหมด"}</button>
    <div className="flex gap-2">
      <button disabled={busy} onClick={onClose} className="h-9 rounded-lg border border-line bg-surface px-3 text-sm font-medium text-fg-2 hover:bg-sunken disabled:opacity-50">ยกเลิก</button>
      <button disabled={busy || !picked.size} onClick={run} className="h-9 rounded-lg bg-accent px-4 text-sm font-semibold text-accent-fg hover:bg-accent-h disabled:opacity-50">{busy ? "กำลังทำ " + progress + "/" + picked.size + "…" : "ใช้กับ " + picked.size + " คน"}</button>
    </div>
  </div>);
  return (<AdminModalShell title={"ใช้ Preset “" + preset.name + "” กับแอดมิน"} sub={(preset.permissions || []).length + " สิทธิ์ · สิทธิ์เดิมของคนที่เลือกจะถูกแทนที่"} onClose={onClose} busy={busy} maxW="max-w-md" footer={footer}>
    {targets.length === 0 ? <div className="py-8 text-center text-sm text-muted">ไม่มีแอดมินที่ตั้งสิทธิ์ได้</div>
    : <div className="space-y-1.5">{targets.map(a => (
      <label key={a.id} className={"flex cursor-pointer items-center gap-3 rounded-lg border px-3 py-2 transition-colors duration-150 " + (picked.has(a.id) ? "border-accent/40 bg-accent-soft" : "border-line bg-sunken hover:bg-surface")}>
        <input type="checkbox" checked={picked.has(a.id)} onChange={() => toggle(a.id)} className="h-4 w-4 accent-[var(--accent)]"/>
        <span className="grid h-7 w-7 shrink-0 place-items-center rounded-full text-xs font-bold text-white" style={{ background: avc(a.username) }}>{initials(a.username)}</span>
        <span className="min-w-0 flex-1"><span className="block truncate text-sm font-medium text-fg">{a.username}</span><span className="block truncate text-xs text-muted">{a.full_name || "-"} · ตอนนี้ {(a.permissions || []).length} สิทธิ์</span></span>
        <Badge tone={ROLE_BADGE[a.role] || "info"}>{a.role}</Badge>
      </label>))}</div>}
  </AdminModalShell>);
}

function PresetManagerModal({ catalog, presetsApi, admins, onClose, onAdminsChanged, toast }) {
  const [editing, setEditing] = useState(null);
  const [applying, setApplying] = useState(null);
  const [busy, setBusy] = useState(false);
  const remove = async (p) => {
    if (!window.confirm("ลบ Preset \"" + p.name + "\"? (ไม่กระทบสิทธิ์ที่ให้แอดมินไปแล้ว)")) return;
    setBusy(true);
    try { await presetsApi.saveAll(presetsApi.presets.filter(x => x.name !== p.name)); }
    catch (e) { toast(e.message || "ลบไม่สำเร็จ"); }
    finally { setBusy(false); }
  };
  const list = presetsApi.presets;
  const footer = (<div className="flex items-center justify-between gap-2">
    <span className="text-xs text-muted tnum">{list.length} Preset</span>
    <button onClick={() => setEditing({})} className="flex h-9 items-center gap-1.5 rounded-lg bg-accent px-3.5 text-sm font-semibold text-accent-fg hover:bg-accent-h"><Ic d={I.plus} size={16} sw={2}/> สร้าง Preset</button>
  </div>);
  return (<>
    <AdminModalShell title="Preset สิทธิ์" sub="ตั้งชุดสิทธิ์ไว้ครั้งเดียว แล้วใช้กับแอดมินใหม่ หรือกับหลายคนพร้อมกัน" onClose={onClose} busy={busy} footer={footer}>
      {presetsApi.loading ? <div className="space-y-2">{Array.from({ length: 3 }).map((_, i) => <div key={i} className="skel h-16 w-full"/>)}</div>
      : list.length === 0 ? <div className="py-12 text-center"><div className="text-sm font-medium text-fg-2">ยังไม่มี Preset</div><div className="mt-1 text-xs text-muted">กด “สร้าง Preset” เช่น ฝ่ายการเงิน, CS, การตลาด</div></div>
      : <div className="space-y-2">{list.map(p => {
        const perms = p.permissions || [];
        return (<div key={p.name} className="rounded-xl border border-line bg-sunken px-3.5 py-3">
          <div className="flex items-center gap-2">
            <div className="min-w-0 flex-1"><div className="truncate text-sm font-semibold text-fg">{p.name}</div><div className="text-xs text-muted tnum">{perms.length} สิทธิ์</div></div>
            <button disabled={busy} onClick={() => setApplying(p)} className="h-8 shrink-0 rounded-lg border border-accent/40 px-2.5 text-xs font-semibold text-accent hover:bg-accent-soft disabled:opacity-50">ใช้กับแอดมิน</button>
            <button disabled={busy} title="แก้ไข" onClick={() => setEditing(p)} className="grid h-8 w-8 shrink-0 place-items-center rounded-lg border border-line text-fg-2 hover:bg-surface disabled:opacity-50"><Ic d={I.edit} size={14}/></button>
            <button disabled={busy} title="ลบ" onClick={() => remove(p)} className="grid h-8 w-8 shrink-0 place-items-center rounded-lg border hover:bg-surface disabled:opacity-50" style={{ borderColor: "var(--danger)", color: "var(--danger)" }}><Ic d={I.trash} size={14}/></button>
          </div>
          <div className="mt-2 flex flex-wrap gap-1">
            {perms.slice(0, 8).map(k => <span key={k} className="rounded-md bg-surface px-1.5 py-0.5 text-[11px] text-fg-2">{catalog.labelOf(k)}</span>)}
            {perms.length > 8 && <span className="px-1 py-0.5 text-[11px] text-muted">+{perms.length - 8}</span>}
          </div>
        </div>);
      })}</div>}
    </AdminModalShell>
    {editing && <PresetEditModal preset={editing.name ? editing : null} catalog={catalog} presetsApi={presetsApi} onClose={() => setEditing(null)} toast={toast}/>}
    {applying && <PresetApplyModal preset={applying} admins={admins} onClose={() => setApplying(null)} onDone={onAdminsChanged} toast={toast}/>}
  </>);
}

function AdminsPage({ toast }) {
  const [rows,setRows]=useState([]);
  const [loading,setLoading]=useState(true);
  const [err,setErr]=useState(null);
  const [addOpen,setAddOpen]=useState(false);
  const [editPerm,setEditPerm]=useState(null);
  const [pwTarget,setPwTarget]=useState(null);
  const [presetOpen,setPresetOpen]=useState(false);
  const canEdit=can("perm_edit_permissions"), canPw=can("perm_change_admin_password");
  /* ยูส Admin แรก (owner): แก้สิทธิ์/เปลี่ยนรหัสผ่านจากตารางได้เฉพาะ isSuper — owner เปลี่ยนรหัสตัวเองที่ช่องด้านบน */
  const canTouchOwner=NX_PERMS.isSuper;
  const [busyId,setBusyId]=useState(null);
  const catalog=usePermCatalog();
  const presetsApi=useRolePresets(toast);
  /* change-my-password */
  const [newPw,setNewPw]=useState("");
  const [pwBusy,setPwBusy]=useState(false);

  const load=useCallback(async()=>{
    setLoading(true); setErr(null);
    try{ const d=await api("/admins"); setRows(Array.isArray(d)?d:[]); }
    catch(e){ if(e.status===401||e.status===403){window.__nxAuthFail&&window.__nxAuthFail();} setErr(e.status===403?"ไม่มีสิทธิ์จัดการแอดมิน":(e.message||"โหลดไม่สำเร็จ")); }
    finally{setLoading(false);}
  },[]);
  useEffect(()=>{load();},[load]);

  const changePw=async()=>{
    if(!newPw||newPw.length<8){toast("รหัสผ่านต้องมีอย่างน้อย 8 ตัวอักษร");return;}
    setPwBusy(true);
    try{ await api("/admins/me/password",{method:"POST",body:{password:newPw}}); toast("เปลี่ยนรหัสผ่านสำเร็จ"); setNewPw(""); }
    catch(e){ if(e.status===401||e.status===403){window.__nxAuthFail&&window.__nxAuthFail();} toast(e.message||"ไม่สำเร็จ"); }
    finally{setPwBusy(false);}
  };

  const toggleStatus=async(a)=>{
    const next=a.status==="active"?"suspended":"active";
    if(!window.confirm((next==="suspended"?"ระงับ":"เปิดใช้งาน")+" แอดมิน "+a.username+"?"))return;
    setBusyId(a.id);
    try{ await api("/admins/"+a.id+"/status",{method:"POST",body:{status:next}}); toast((next==="suspended"?"ระงับ":"เปิดใช้งาน")+"แล้ว"); load(); }
    catch(e){ toast(e.message||"ไม่สำเร็จ"); }
    finally{setBusyId(null);}
  };

  const del=async(a)=>{
    if(!window.confirm("ลบแอดมิน "+a.username+" ถาวร?"))return;
    setBusyId(a.id);
    try{ await api("/admins/"+a.id,{method:"DELETE"}); toast("ลบแล้ว"); load(); }
    catch(e){ toast(e.message||"ไม่สำเร็จ"); }
    finally{setBusyId(null);}
  };

  const reset2fa=async(a)=>{
    if(!window.confirm("รีเซ็ต 2FA ของ "+a.username+"?"))return;
    setBusyId(a.id);
    try{ await api("/admins/"+a.id+"/reset-2fa",{method:"POST",body:{}}); toast("รีเซ็ต 2FA สำเร็จ"); load(); }
    catch(e){ toast(e.message||"ไม่สำเร็จ"); }
    finally{setBusyId(null);}
  };

  const inp="h-9 w-full rounded-lg border border-line bg-sunken px-3 text-sm text-fg focus:border-accent focus:bg-surface focus:outline-none";
  const H=({children,extra=""})=>(<th className={"whitespace-nowrap px-3 py-2.5 text-left text-[13px] font-bold text-fg-2 "+extra}>{children}</th>);

  return (<div className="space-y-4">
    {/* ---------- Change My Password ---------- */}
    <div className="rounded-xl border border-line bg-surface p-4 shadow-sm">
      <div className="text-sm font-semibold text-fg">เปลี่ยนรหัสผ่านของฉัน</div>
      <div className="mt-2 flex items-end gap-2">
        <div className="flex-1 sm:max-w-xs">
          <input type="password" value={newPw} onChange={e=>setNewPw(e.target.value)} placeholder="รหัสผ่านใหม่ (8 ตัวขึ้นไป)" className={inp}/>
        </div>
        <button disabled={pwBusy} onClick={changePw} className="h-9 shrink-0 rounded-lg bg-accent px-4 text-sm font-semibold text-accent-fg hover:bg-accent-h disabled:opacity-50">{pwBusy?"กำลังทำ…":"บันทึก"}</button>
      </div>
    </div>

    {/* ---------- Admin List ---------- */}
    <div className="flex flex-wrap items-center gap-2">
      <button onClick={load} className="flex h-9 items-center gap-1.5 rounded-lg border border-line bg-surface px-3 text-sm font-medium text-fg-2 hover:bg-sunken"><Ic d={I.refresh} size={16} cls={loading?"animate-spin":""}/> รีเฟรช</button>
      {can("perm_edit_permissions") && <button onClick={()=>setAddOpen(true)} className="flex h-9 items-center gap-1.5 rounded-lg bg-accent px-3.5 text-sm font-semibold text-accent-fg shadow-sm hover:bg-accent-h"><Ic d={I.plus} size={17} sw={2}/> เพิ่มแอดมิน</button>}
      {can("perm_edit_permissions") && <button onClick={()=>setPresetOpen(true)} className="flex h-9 items-center gap-1.5 rounded-lg border border-accent/40 bg-surface px-3.5 text-sm font-semibold text-accent hover:bg-accent-soft"><Ic d={I.key} size={16}/> Preset สิทธิ์{presetsApi.presets.length>0 && <span className="tnum font-normal text-muted">({presetsApi.presets.length})</span>}</button>}
      <div className="ml-auto hidden text-xs text-muted sm:block tnum">{rows.length} คน</div>
    </div>

    {err ? (<div className="rounded-xl border border-line bg-surface px-4 py-16 text-center shadow-sm"><div className="mx-auto mb-2 grid h-10 w-10 place-items-center rounded-full text-danger" style={{background:"var(--danger-soft)"}}><Ic d={I.warn2} size={20}/></div><div className="text-sm font-medium text-fg-2">{err}</div><button onClick={load} className="mt-3 rounded-lg border border-line px-3 py-1.5 text-xs font-medium text-fg-2 hover:bg-sunken">ลองใหม่</button></div>)
    : (<div className="overflow-hidden rounded-xl border border-line bg-surface shadow-sm"><div className="overflow-x-auto"><table className="w-full min-w-[820px] border-collapse text-sm">
      <thead className="border-b-2 border-accent/25 bg-accent-soft"><tr><H>Username</H><H>ชื่อแสดง</H><H>Role</H><H>Status</H><H>2FA</H><H>สร้างเมื่อ</H>{(canEdit||canPw)&&<H extra="text-right pr-4">จัดการ</H>}</tr></thead>
      <tbody>
        {loading ? Array.from({length:5}).map((_,i)=>(<tr key={i} className="border-b border-line-soft"><td className="px-3 py-3" colSpan={(canEdit||canPw)?7:6}><div className="skel h-5 w-full"/></td></tr>))
        : rows.length===0 ? (<tr><td colSpan={(canEdit||canPw)?7:6} className="px-4 py-16 text-center text-sm font-medium text-fg-2">ยังไม่มีแอดมิน</td></tr>)
        : rows.map((a,i)=>(<tr key={a.id||i} className="border-b border-line-soft transition-colors last:border-0 hover:bg-sunken/50">
          <td className="px-3 py-2.5">
            <div className="flex items-center gap-2">
              <span className="grid h-7 w-7 shrink-0 place-items-center rounded-full text-xs font-bold text-white" style={{background:avc(a.username)}}>{initials(a.username)}</span>
              <span className="truncate font-medium text-fg">{a.username}</span>
              {a.is_owner && <Badge tone="accent">owner</Badge>}
            </div>
          </td>
          <td className="px-3 text-fg-2">{a.full_name||"-"}</td>
          <td className="px-3"><div className="flex items-center gap-2"><Badge tone={ROLE_BADGE[a.role]||"info"}>{a.role}</Badge><span className="text-xs text-muted tnum">{(a.permissions||[]).length} สิทธิ์</span></div></td>
          <td className="px-3"><Badge tone={STATUS_BADGE[a.status]||"info"}>{a.status}</Badge></td>
          <td className="px-3 text-xs text-fg-2">{a.admin_totp_enabled ? <span className="text-success font-medium">เปิด</span> : <span className="text-muted">ปิด</span>}</td>
          <td className="whitespace-nowrap px-3 text-xs text-fg-2">{fmtDT(a.created_at)}</td>
          {(canEdit||canPw) && <td className="pr-4"><div className="flex items-center justify-end gap-1">
            {canEdit && (!a.is_owner||canTouchOwner) && <button disabled={busyId===a.id} title="แก้ไขสิทธิ์" onClick={()=>setEditPerm(a)} className="grid h-7 w-7 place-items-center rounded-lg border border-line text-fg-2 hover:bg-sunken disabled:opacity-50"><Ic d={I.edit} size={14}/></button>}
            {canPw && (!a.is_owner||canTouchOwner) && <button disabled={busyId===a.id} title="เปลี่ยนรหัสผ่าน" onClick={()=>setPwTarget(a)} className="grid h-7 w-7 place-items-center rounded-lg border border-line text-fg-2 hover:bg-sunken disabled:opacity-50"><Ic d={I.lock} size={14}/></button>}
            {canEdit && <button disabled={busyId===a.id} title={a.status==="active"?"ระงับ":"เปิดใช้งาน"} onClick={()=>toggleStatus(a)} className="grid h-7 w-7 place-items-center rounded-lg border hover:bg-sunken disabled:opacity-50" style={{borderColor:a.status==="active"?"var(--warn)":"var(--success)",color:a.status==="active"?"var(--warn)":"var(--success)"}}><Ic d={a.status==="active"?I.ban:I.check} size={14}/></button>}
            {canEdit && a.admin_totp_enabled && <button disabled={busyId===a.id} title="รีเซ็ต 2FA" onClick={()=>reset2fa(a)} className="grid h-7 w-7 place-items-center rounded-lg border border-line text-fg-2 hover:bg-sunken disabled:opacity-50"><Ic d={I.key} size={14}/></button>}
            {canEdit && !a.is_owner && <button disabled={busyId===a.id} title="ลบ" onClick={()=>del(a)} className="grid h-7 w-7 place-items-center rounded-lg border hover:bg-sunken disabled:opacity-50" style={{borderColor:"var(--danger)",color:"var(--danger)"}}><Ic d={I.trash} size={14}/></button>}
          </div></td>}
        </tr>))}
      </tbody>
    </table></div></div>)}

    {addOpen && <CreateAdminModal presets={presetsApi.presets} onClose={()=>setAddOpen(false)} onDone={load} toast={toast}/>}
    {pwTarget && <AdminPasswordModal admin={pwTarget} onClose={()=>setPwTarget(null)} toast={toast}/>}
    {editPerm && <PermModal admin={editPerm} catalog={catalog} presetsApi={presetsApi} onClose={()=>setEditPerm(null)} onDone={load} toast={toast}/>}
    {presetOpen && <PresetManagerModal catalog={catalog} presetsApi={presetsApi} admins={rows} onClose={()=>setPresetOpen(false)} onAdminsChanged={load} toast={toast}/>}
  </div>);
}
