/* admin.jsx — หน้าของผู้ดูแล/บัญชี: ภาพรวมทุกฝ่าย, รายการเกินเกณฑ์, ตั้งค่า (เกณฑ์ ผู้ใช้ รหัสบัญชี), export Excel */

const FLAG_LABEL = { red: 'แดง', yellow: 'เหลือง', none: '' };

/** export Excel 3 ชีต: สรุปรายฝ่าย / รายละเอียดทุกบรรทัด / รายการเกินเกณฑ์ */
function exportExcel(all) {
  const cfg = all.round;
  const by = 'งบปี ' + cfg.baseYear, ac = 'ใช้จริง ' + actualRange(cfg), fc = 'คาดการณ์ทั้งปี ' + cfg.baseYear, rq = 'ขอปี ' + cfg.year;
  const pct1 = (p) => (p == null || !isFinite(p) ? null : Math.round(p * 10) / 10);
  const summary = [['รหัสฝ่าย', 'ชื่อฝ่าย', 'สถานะ', 'จำนวนรายการ', 'กรอกแล้ว', by, ac, fc, rq, 'ส่วนต่าง', '% เทียบงบ', 'แถบแดง', 'แถบเหลือง']];
  const lineHead = ['รหัสฝ่าย', 'ชื่อฝ่าย', 'รหัสบัญชี', 'ชื่อบัญชี', 'หมวด', 'ผู้กรอก', by, ac, fc];
  const detail = [[...lineHead, ...R.MONTHS_TH, rq, 'ส่วนต่าง', '% เทียบงบ', 'ธง', 'เหตุผล/คำอธิบาย']];
  const flags = [[...lineHead, rq, 'ส่วนต่าง', '% เทียบงบ', 'ธง', 'เหตุผล/คำอธิบาย']];
  for (const d of all.depts) {
    const s = summarize(d.lines, cfg);
    // ส่วนต่าง/% ของทั้งฝ่ายมีความหมายเมื่อกรอกครบทุกรายการแล้วเท่านั้น
    const done = s.n > 0 && s.filled === s.n;
    summary.push([d.code, d.name, (STATUS[d.status] || STATUS.draft).label, s.n, s.filled, s.B, s.A, s.P, s.R, done ? R.round2(s.R - s.B) : null, done ? pct1(s.pct) : null, s.red, s.yellow]);
    for (const l of d.lines) {
      const ev = R.evaluateLine(l, cfg);
      const left = [d.code, d.name, l.acct, l.name, l.cat, l.filler === 'central' ? 'บัญชี' : 'ฝ่าย', ev.B, ev.A, ev.P];
      const right = [ev.filled ? ev.R : null, ev.filled ? ev.diff : null, ev.filled ? pct1(ev.pct) : null, FLAG_LABEL[ev.flag], l.note];
      detail.push([...left, ...(l.months || Array(12).fill(null)), ...right]);
      if (ev.flag !== 'none') flags.push([...left, ...right]);
    }
  }
  const wb = XLSX.utils.book_new();
  const add = (name, aoa, widths) => {
    const ws = XLSX.utils.aoa_to_sheet(aoa);
    ws['!cols'] = widths.map((wch) => ({ wch }));
    XLSX.utils.book_append_sheet(wb, ws, name);
  };
  add('สรุปรายฝ่าย', summary, [10, 38, 16, 12, 10, 16, 16, 18, 16, 16, 10, 9, 10]);
  add('รายละเอียด', detail, [10, 30, 10, 40, 26, 8, 14, 14, 18, ...Array(12).fill(11), 14, 14, 10, 8, 50]);
  add('เกินเกณฑ์', flags, [10, 30, 10, 40, 26, 8, 14, 14, 18, 14, 14, 10, 8, 60]);
  const d = new Date();
  const stamp = d.getFullYear() + String(d.getMonth() + 1).padStart(2, '0') + String(d.getDate()).padStart(2, '0');
  XLSX.writeFile(wb, `งบประมาณ${cfg.year}_ทุกฝ่าย_${stamp}.xlsx`);
}

function useAdminAll() {
  const [all, setAll] = useState(null);
  const [err, setErr] = useState('');
  const load = useCallback(() => {
    API.adminAll().then((d) => { setAll(d); setErr(''); }).catch((e) => setErr(e.message));
  }, []);
  useEffect(() => { load(); }, [load]);
  return { all, err, load };
}

function AdminHome({ go }) {
  const { all, err, load } = useAdminAll();
  if (err) return <div className="card pad"><p className="err">{err}</p><p><button className="btn sm" onClick={load}>ลองใหม่</button></p></div>;
  if (!all) return <div className="boot">กำลังโหลด…</div>;
  const cfg = all.round;
  const rows = all.depts.map((d) => ({ d, s: summarize(d.lines, cfg) }));
  const T = rows.reduce((t, r) => {
    ['n', 'filled', 'own', 'ownFilled', 'central', 'centralFilled', 'B', 'A', 'P', 'R', 'red', 'yellow', 'noteMissing'].forEach((k) => { t[k] += r.s[k]; });
    return t;
  }, { n: 0, filled: 0, own: 0, ownFilled: 0, central: 0, centralFilled: 0, B: 0, A: 0, P: 0, R: 0, red: 0, yellow: 0, noteMissing: 0 });
  const sent = rows.filter((r) => r.d.status === 'submitted' || r.d.status === 'locked').length;
  const pct = (s) => (s.filled && s.B > 0 ? fmtPct(((s.R - s.B) / s.B) * 100) : '');
  return (
    <div>
      <div className="head">
        <div className="grow">
          <h1>ภาพรวมงบประมาณปี {cfg.year}</h1>
          <div className="mute small">อ้างอิงงบและยอดใช้จริงปี {cfg.baseYear} ({actualRange(cfg)}) · คัดลอกยอดเมื่อ {fmtDateTime(cfg.refImportedAt)}</div>
        </div>
        <button className="btn" onClick={() => exportExcel(all)}><Icon name="download" />Export Excel</button>
      </div>

      <div className="tiles">
        <div className="tile t-status"><div className="k">ฝ่ายที่ส่งแล้ว</div><div className="v">{sent}/{rows.length}</div><div className="s">ล็อกแล้ว {rows.filter((r) => r.d.status === 'locked').length} ฝ่าย</div></div>
        <div className="tile t-budget"><div className="k">งบปีนี้รวม</div><div className="v">{fmt(T.B)}</div><div className="s">ใช้จริง {actualRange(cfg)} {fmt(T.A)}</div></div>
        <div className="tile t-forecast"><div className="k">คาดการณ์ใช้จริงทั้งปี</div><div className="v">{fmt(T.P)}</div><div className="s">{T.B > 0 ? Math.round((T.P / T.B) * 100) + '% ของงบปีนี้ · ' : ''}เฉลี่ยต่อเดือน × 12</div></div>
        <div className="tile t-request"><div className="k">ขอปีหน้ารวม (ที่กรอกแล้ว)</div><div className="v">{fmt(T.R)}</div><div className="s">กรอกแล้ว {T.filled}/{T.n} รายการ</div></div>
        <div className="tile t-flag"><div className="k">รายการที่ต้องดู</div><div className="v">{T.red} <span className="s">แดง</span> · {T.yellow} <span className="s">เหลือง</span></div><div className="s">{T.noteMissing ? `ยังไม่ใส่เหตุผล ${T.noteMissing} รายการ` : T.red ? 'แดงทุกรายการมีเหตุผลแล้ว' : 'ยังไม่มีรายการแดง'}</div></div>
      </div>

      <div className="card tw">
        <table className="t">
          <thead>
            <tr>
              <th>ฝ่าย</th><th>สถานะ</th><th className="num">ฝ่ายกรอก</th><th className="num">บัญชีกรอก</th>
              <th className="num">งบปีนี้</th><th className="num">ใช้จริง {actualRange(cfg)}</th><th className="num">คาดการณ์ทั้งปี</th><th className="num">ขอปีหน้า</th>
              <th className="num">เทียบงบ</th><th className="num">แดง</th><th className="num">เหลือง</th>
            </tr>
          </thead>
          <tbody>
            {rows.map(({ d, s }) => (
              <tr key={d.code} className="click" onClick={() => go('dept/' + encodeURIComponent(d.code))} title="เปิดดูรายการของฝ่ายนี้">
                <td><b>{d.code}</b> <span className="mute small">{d.name}</span></td>
                <td><StatusBadge status={d.status} /></td>
                <td className="num">{s.ownFilled}/{s.own}</td>
                <td className="num">{s.central ? s.centralFilled + '/' + s.central : '–'}</td>
                <td className="num">{fmt(s.B)}</td>
                <td className="num">{fmt(s.A)}</td>
                <td className="num">{fmt(s.P)}</td>
                <td className="num">{s.filled ? fmt(s.R) : '–'}</td>
                <td className="num">{s.filled === s.n ? pct(s) : ''}</td>
                <td className={'num' + (s.red ? ' red' : '')}>{s.red || ''}</td>
                <td className={'num' + (s.yellow ? ' yellow' : '')}>{s.yellow || ''}</td>
              </tr>
            ))}
          </tbody>
          <tfoot>
            <tr>
              <td colSpan={2}>รวม {rows.length} ฝ่าย</td>
              <td className="num">{T.ownFilled}/{T.own}</td><td className="num">{T.centralFilled}/{T.central}</td>
              <td className="num">{fmt(T.B)}</td><td className="num">{fmt(T.A)}</td><td className="num">{fmt(T.P)}</td><td className="num">{fmt(T.R)}</td>
              <td /><td className="num">{T.red || ''}</td><td className="num">{T.yellow || ''}</td>
            </tr>
          </tfoot>
        </table>
      </div>
      <p className="mute small" style={{ marginTop: 10 }}>"เทียบงบ" แสดงเมื่อฝ่ายนั้นกรอกครบทุกรายการแล้ว · คลิกแถวเพื่อเปิดดูรายละเอียด ตีกลับ หรือล็อก</p>
    </div>
  );
}

function AdminFlags({ go }) {
  const { all, err, load } = useAdminAll();
  const [kind, setKind] = useState('red');
  const [dept, setDept] = useState('');
  if (err) return <div className="card pad"><p className="err">{err}</p><p><button className="btn sm" onClick={load}>ลองใหม่</button></p></div>;
  if (!all) return <div className="boot">กำลังโหลด…</div>;
  const cfg = all.round;
  const items = [];
  for (const d of all.depts) {
    for (const l of d.lines) {
      const ev = R.evaluateLine(l, cfg);
      if (ev.flag !== 'none') items.push({ d, l, ev });
    }
  }
  const shown = items
    .filter((x) => (kind === 'all' || x.ev.flag === kind) && (!dept || x.d.code === dept))
    .sort((a, b) => b.ev.diff - a.ev.diff);
  const nRed = items.filter((x) => x.ev.flag === 'red').length, nYellow = items.length - nRed;
  return (
    <div>
      <div className="head">
        <div className="grow">
          <h1>รายการเกินเกณฑ์</h1>
          <div className="mute small">
            แดง = ขอเกินงบปีนี้มากกว่า {cfg.redPct}% และเกิน {fmt(cfg.redMinBaht)} บาท หรือปีนี้ไม่มีงบ ·
            เหลือง = ปีนี้ใช้จริงไม่ถึง {cfg.yellowUsagePct}% ของงบ แต่ขอเท่าเดิมหรือมากกว่า
          </div>
        </div>
        <button className="btn" onClick={() => exportExcel(all)}><Icon name="download" />Export Excel</button>
      </div>
      <div className="card">
        <div className="toolbar">
          <div className="chips">
            <button className={'chip red' + (kind === 'red' ? ' on' : '')} onClick={() => setKind('red')}>แดง {nRed}</button>
            <button className={'chip yellow' + (kind === 'yellow' ? ' on' : '')} onClick={() => setKind('yellow')}>เหลือง {nYellow}</button>
            <button className={'chip' + (kind === 'all' ? ' on' : '')} onClick={() => setKind('all')}>ทั้งคู่ {items.length}</button>
          </div>
          <select value={dept} onChange={(e) => setDept(e.target.value)} aria-label="กรองตามฝ่าย" style={{ height: 30, fontSize: 13 }}>
            <option value="">ทุกฝ่าย</option>
            {all.depts.map((d) => <option key={d.code} value={d.code}>{d.code}</option>)}
          </select>
        </div>
        <div className="tw">
          <table className="t">
            <thead>
              <tr>
                <th>ฝ่าย</th><th>รหัสบัญชี</th><th className="num">งบปีนี้</th><th className="num">ใช้จริง</th><th className="num">คาดการณ์ทั้งปี</th>
                <th className="num">ขอปีหน้า</th><th className="num">ส่วนต่าง</th><th className="num">เทียบงบ</th><th>เหตุผล / คำอธิบาย</th>
              </tr>
            </thead>
            <tbody>
              {shown.length === 0 && <tr><td colSpan={9} className="empty">ยังไม่มีรายการในกลุ่มนี้</td></tr>}
              {shown.map(({ d, l, ev }) => (
                <tr key={d.code + l.acct} className="click" onClick={() => go('dept/' + encodeURIComponent(d.code))}>
                  <td><b>{d.code}</b></td>
                  <td><span className="mute small">{l.acct}</span> {l.name}</td>
                  <td className="num">{fmt(ev.B)}</td>
                  <td className="num">{fmt(ev.A)}</td>
                  <td className="num">{ev.P == null ? '–' : fmt(ev.P)}</td>
                  <td className="num">{fmt(ev.R)}</td>
                  <td className={'num ' + ev.flag}>{ev.diff > 0 ? '+' : ''}{fmt(ev.diff)}</td>
                  <td className={'num ' + ev.flag}>{ev.pct == null ? 'ใหม่' : fmtPct(ev.pct)}</td>
                  <td style={{ minWidth: 220 }}>{l.note || (ev.flag === 'red' ? <span className="err">ยังไม่ใส่เหตุผล</span> : <span className="mute small">–</span>)}</td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      </div>
    </div>
  );
}

/* ═══════════════════════ ตั้งค่า ═══════════════════════ */

/** ยอดอ้างอิง: คัดลอกงบ+ใช้จริงจากเว็บ fin มาเก็บไว้ ณ ตอนนั้น (เปิดรอบแรก หรืออัปเดตยอดของรอบปัจจุบัน) */
function ReferenceCard({ round, onChanged }) {
  const [info, setInfo] = useState(null);
  const [year, setYear] = useState(String(new Date().getFullYear() + 543 + 1));
  const [confirm, setConfirm] = useState(false);
  const [busy, setBusy] = useState(false);
  useEffect(() => { API.reference().then(setInfo).catch((e) => toast(e.message, 'error')); }, []);
  const pull = async () => {
    setBusy(true);
    try {
      const r = await API.pullReference(round ? null : Number(year));
      setConfirm(false);
      toast(`คัดลอกยอดจากเว็บ fin แล้ว: ${r.depts} ฝ่าย ${r.lines} บรรทัด ใช้จริงถึง ${R.MONTHS_TH[Math.max(0, r.actualMonths - 1)]}`);
      onChanged(r.round);
    } catch (e) { toast(e.message, 'error'); } finally { setBusy(false); }
  };
  const linked = info && info.finConfigured;
  return (
    <div className="card pad" style={{ marginBottom: 14 }}>
      <h2>ยอดอ้างอิงจากเว็บ fin</h2>
      {round ? (
        <p className="mute small" style={{ marginBottom: 12 }}>
          ตอนนี้ใช้ยอดที่คัดลอกเมื่อ {fmtDateTime(round.refImportedAt)} · งบและใช้จริงปี {round.baseYear} ({actualRange(round)}) · จาก {round.refSource || '–'}
        </p>
      ) : (
        <p className="mute small" style={{ marginBottom: 12 }}>ยังไม่มียอดอ้างอิง คัดลอกงบและยอดใช้จริงของปีนี้จากเว็บ fin เพื่อเปิดรอบขออนุมัติงบ</p>
      )}
      <div className="formgrid">
        {!round && (
          <label className="field">ปีงบประมาณที่จะขอ (พ.ศ.)<input type="text" inputMode="numeric" value={year} onChange={(e) => setYear(e.target.value)} /></label>
        )}
        <div>
          <button className="btn primary" disabled={!linked || busy} onClick={() => setConfirm(true)}>
            <Icon name="download" />{round ? 'อัปเดตยอดจากเว็บ fin' : 'คัดลอกยอดและเปิดรอบ'}
          </button>
        </div>
      </div>
      {info && !linked && (
        <p className="small" style={{ marginTop: 10, color: 'var(--yellow)' }}>
          เซิร์ฟเวอร์นี้ยังไม่ได้เชื่อมกับฐานข้อมูลของเว็บ fin จึงกดดึงยอดไม่ได้ (ผู้ดูแลเซิร์ฟเวอร์ตั้งค่า FIN_DATABASE_URL ตามคู่มือ DEPLOY.md)
        </p>
      )}
      {confirm && (
        <Modal
          title={round ? 'อัปเดตยอดอ้างอิง' : 'เปิดรอบขออนุมัติงบ'} onClose={() => setConfirm(false)}
          footer={<><button className="btn" onClick={() => setConfirm(false)}>ยกเลิก</button><button className="btn primary" disabled={busy} onClick={pull}>{busy ? 'กำลังคัดลอก…' : 'คัดลอกยอด'}</button></>}
        >
          {round ? (
            <>
              <p>คัดลอกงบและยอดใช้จริงล่าสุดจากเว็บ fin มาแทนยอดอ้างอิงชุดปัจจุบันของทุกฝ่าย</p>
              <p className="mute small">ยอดที่ฝ่ายกรอกไว้แล้วไม่ถูกแตะ แต่แถบแดง/เหลืองและยอดคาดการณ์จะคำนวณใหม่ตามยอดล่าสุด</p>
            </>
          ) : (
            <p>คัดลอกงบและยอดใช้จริงจากเว็บ fin แล้วเปิดรอบขออนุมัติงบปี {year}</p>
          )}
        </Modal>
      )}
    </div>
  );
}

function ThresholdCard({ round, onRound }) {
  const [f, setF] = useState({ redPct: String(round.redPct), redMinBaht: String(round.redMinBaht), yellowUsagePct: String(round.yellowUsagePct), status: round.status });
  const [busy, setBusy] = useState(false);
  const [msg, setMsg] = useState('');
  const set = (k) => (e) => { setF({ ...f, [k]: e.target.value }); setMsg(''); };
  const save = async () => {
    const nums = { redPct: parseNum(f.redPct), redMinBaht: parseNum(f.redMinBaht), yellowUsagePct: parseNum(f.yellowUsagePct) };
    if (Object.values(nums).some((x) => x == null || Number.isNaN(x))) { setMsg('ใส่ตัวเลขให้ครบทุกช่อง'); return; }
    setBusy(true);
    try {
      const r = await API.saveSettings({ ...nums, status: f.status });
      onRound(r.round); toast('บันทึกเกณฑ์แล้ว มีผลกับทุกฝ่ายทันที');
    } catch (e) { setMsg(e.message); } finally { setBusy(false); }
  };
  return (
    <div className="card pad" style={{ marginBottom: 14 }}>
      <h2>เกณฑ์เตือน</h2>
      <p className="mute small" style={{ marginBottom: 12 }}>เปลี่ยนแล้วมีผลทันทีกับทุกฝ่าย รวมรายการที่กรอกไปแล้ว</p>
      <div className="formgrid">
        <label className="field">แดง: ขอเกินงบปีนี้มากกว่า (%)<input type="text" inputMode="decimal" value={f.redPct} onChange={set('redPct')} /></label>
        <label className="field">แดง: และเกิน (บาท)<input type="text" inputMode="decimal" value={f.redMinBaht} onChange={set('redMinBaht')} /></label>
        <label className="field">เหลือง: ใช้จริงไม่ถึง (% ของงบทั้งปี)<input type="text" inputMode="decimal" value={f.yellowUsagePct} onChange={set('yellowUsagePct')} /></label>
        <label className="field">รอบขออนุมัติงบ
          <select value={f.status} onChange={set('status')}>
            <option value="open">เปิดให้กรอก</option>
            <option value="closed">ปิดรอบ (ดูได้อย่างเดียว)</option>
          </select>
        </label>
        <div><button className="btn primary" disabled={busy} onClick={save}>บันทึกเกณฑ์</button></div>
      </div>
      {msg && <p className="err" style={{ marginTop: 8 }}>{msg}</p>}
    </div>
  );
}

function UsersCard({ me }) {
  const [data, setData] = useState(null);
  const [err, setErr] = useState('');
  const [nu, setNu] = useState({ username: '', displayName: '', role: 'dept', dept: '' });
  const [busy, setBusy] = useState(false);
  const [secret, setSecret] = useState(null);
  const load = useCallback(() => API.users().then(setData).catch((e) => setErr(e.message)), []);
  useEffect(() => { load(); }, [load]);
  if (err) return <div className="card pad"><p className="err">{err}</p></div>;
  if (!data) return <div className="card pad mute">กำลังโหลดผู้ใช้…</div>;

  const run = async (fn) => {
    setBusy(true);
    try { await fn(); await load(); } catch (e) { toast(e.message, 'error'); } finally { setBusy(false); }
  };
  const create = () => run(async () => {
    const r = await API.createUser(nu);
    setSecret({ username: r.user.username, password: r.tempPassword });
    setNu({ username: '', displayName: '', role: nu.role, dept: '' });
  });
  const reset = (u) => run(async () => {
    const r = await API.resetPassword(u.id);
    setSecret({ username: u.username, password: r.tempPassword });
  });
  const withUser = new Set(data.users.filter((u) => u.role === 'dept' && u.active).map((u) => u.dept));
  const missing = data.depts.filter((d) => !withUser.has(d.code));

  return (
    <div className="card" style={{ marginBottom: 14 }}>
      <div className="pad" style={{ padding: '16px 18px 12px' }}>
        <h2>ผู้ใช้</h2>
        <p className="mute small" style={{ marginBottom: 12 }}>สร้างผู้ใช้แล้วระบบจะให้รหัสผ่านชั่วคราว ผู้ใช้ต้องตั้งรหัสใหม่เองตอนเข้าครั้งแรก</p>
        <div className="formgrid">
          <label className="field">ชื่อผู้ใช้ (a-z 0-9)<input type="text" value={nu.username} onChange={(e) => setNu({ ...nu, username: e.target.value })} placeholder="hrd.somchai" autoComplete="off" /></label>
          <label className="field">ชื่อที่แสดง<input type="text" value={nu.displayName} onChange={(e) => setNu({ ...nu, displayName: e.target.value })} placeholder="สมชาย ใจดี" autoComplete="off" /></label>
          <label className="field">ประเภท
            <select value={nu.role} onChange={(e) => setNu({ ...nu, role: e.target.value })}>
              <option value="dept">ฝ่าย (กรอกเฉพาะฝ่ายตัวเอง)</option>
              <option value="central">บัญชี (กรอกรายการส่วนกลางทุกฝ่าย)</option>
              <option value="admin">ผู้ดูแล (เห็นและทำได้ทุกอย่าง)</option>
            </select>
          </label>
          {nu.role === 'dept' && (
            <label className="field">ฝ่าย
              <select value={nu.dept} onChange={(e) => setNu({ ...nu, dept: e.target.value })}>
                <option value="">เลือกฝ่าย</option>
                {data.depts.map((d) => <option key={d.code} value={d.code}>{d.code} · {d.name}</option>)}
              </select>
            </label>
          )}
          <div><button className="btn primary" disabled={busy || !nu.username.trim() || (nu.role === 'dept' && !nu.dept)} onClick={create}><Icon name="plus" />สร้างผู้ใช้</button></div>
        </div>
        {missing.length > 0 && (
          <p className="small" style={{ marginTop: 10, color: 'var(--yellow)' }}>ฝ่ายที่ยังไม่มีผู้ใช้: {missing.map((d) => d.code).join(', ')}</p>
        )}
      </div>
      <div className="tw">
        <table className="t">
          <thead><tr><th>ชื่อผู้ใช้</th><th>ชื่อที่แสดง</th><th>ประเภท</th><th>ฝ่าย</th><th>สถานะ</th><th>เข้าใช้ล่าสุด</th><th /></tr></thead>
          <tbody>
            {data.users.map((u) => (
              <tr key={u.id}>
                <td><b>{u.username}</b></td>
                <td>{u.displayName}</td>
                <td>{ROLE_LABEL[u.role] || u.role}</td>
                <td>{u.dept || '–'}</td>
                <td>
                  {!u.active ? <span className="badge red">ปิดใช้งาน</span>
                    : u.mustChangePassword ? <span className="badge yellow">รอตั้งรหัสใหม่</span>
                    : <span className="badge green">ใช้งาน</span>}
                </td>
                <td className="mute small">{u.lastLoginAt ? fmtDateTime(u.lastLoginAt) : 'ยังไม่เคย'}</td>
                <td className="nowrap" style={{ textAlign: 'right' }}>
                  <button className="btn sm" disabled={busy} onClick={() => reset(u)}><Icon name="key" />รีเซ็ตรหัสผ่าน</button>{' '}
                  {u.username !== me.username && (
                    <button className={'btn sm' + (u.active ? ' danger' : '')} disabled={busy} onClick={() => run(() => API.updateUser(u.id, { active: !u.active }))}>
                      {u.active ? 'ปิดใช้งาน' : 'เปิดใช้งาน'}
                    </button>
                  )}
                </td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>
      {secret && (
        <Modal title="รหัสผ่านชั่วคราว" onClose={() => setSecret(null)} footer={<button className="btn primary" onClick={() => setSecret(null)}>ส่งให้ผู้ใช้แล้ว</button>}>
          <p>ผู้ใช้ <b>{secret.username}</b></p>
          <div className="secret">{secret.password}</div>
          <p className="mute small">รหัสนี้แสดงครั้งเดียว ระบบไม่ได้เก็บตัวรหัสไว้ ส่งให้ผู้ใช้โดยตรง แล้วผู้ใช้จะถูกบังคับให้ตั้งรหัสใหม่ตอนเข้าครั้งแรก</p>
          <div>
            <button className="btn sm" onClick={() => navigator.clipboard.writeText(secret.password).then(() => toast('คัดลอกแล้ว'), () => toast('คัดลอกไม่ได้ ให้เลือกข้อความแล้วคัดลอกเอง', 'error'))}>
              <Icon name="copy" />คัดลอก
            </button>
          </div>
        </Modal>
      )}
    </div>
  );
}

function AccountsCard() {
  const [rows, setRows] = useState(null);
  const [edits, setEdits] = useState({});
  const [q, setQ] = useState('');
  const [who, setWho] = useState('all');
  const [busy, setBusy] = useState(false);
  const load = useCallback(() => API.accounts().then((d) => { setRows(d.accounts); setEdits({}); }).catch((e) => toast(e.message, 'error')), []);
  useEffect(() => { load(); }, [load]);
  if (!rows) return <div className="card pad mute">กำลังโหลดรหัสบัญชี…</div>;

  const cur = (a) => ({ filler: a.filler, redPct: a.redPct == null ? '' : String(a.redPct), ...(edits[a.code] || {}) });
  const edit = (code, patch) => setEdits((e) => ({ ...e, [code]: { ...(e[code] || {}), ...patch } }));
  const changed = rows.filter((a) => {
    const c = cur(a);
    return c.filler !== a.filler || c.redPct !== (a.redPct == null ? '' : String(a.redPct));
  });
  const needle = q.trim().toLowerCase();
  const shown = rows.filter((a) => {
    const c = cur(a);
    if (who !== 'all' && c.filler !== who) return false;
    return !needle || a.code.includes(needle) || a.name.toLowerCase().includes(needle) || a.cat.toLowerCase().includes(needle);
  });
  const save = async () => {
    const items = [];
    for (const a of changed) {
      const c = cur(a);
      const p = c.redPct.trim() === '' ? null : parseNum(c.redPct);
      if (Number.isNaN(p)) { toast(`รหัส ${a.code}: เกณฑ์ % ไม่ถูกต้อง`, 'error'); return; }
      items.push({ code: a.code, filler: c.filler, redPct: p });
    }
    setBusy(true);
    try { await API.saveAccounts(items); toast(`บันทึก ${items.length} รหัสแล้ว`); await load(); }
    catch (e) { toast(e.message, 'error'); } finally { setBusy(false); }
  };
  const nCentral = rows.filter((a) => cur(a).filler === 'central').length;

  return (
    <div className="card">
      <div style={{ padding: '16px 18px 4px' }}>
        <h2>รหัสบัญชี: ใครกรอก</h2>
        <p className="mute small">"บัญชีกรอก" = ฝ่ายเห็นตัวเลขแต่แก้ไม่ได้ ผู้ใช้ประเภทบัญชีเป็นคนใส่ให้ทุกฝ่าย · เกณฑ์ % เฉพาะรหัส เว้นว่าง = ใช้เกณฑ์กลาง</p>
      </div>
      <div className="toolbar">
        <div className="chips">
          <button className={'chip' + (who === 'all' ? ' on' : '')} onClick={() => setWho('all')}>ทั้งหมด {rows.length}</button>
          <button className={'chip' + (who === 'dept' ? ' on' : '')} onClick={() => setWho('dept')}>ฝ่ายกรอก {rows.length - nCentral}</button>
          <button className={'chip' + (who === 'central' ? ' on' : '')} onClick={() => setWho('central')}>บัญชีกรอก {nCentral}</button>
        </div>
        <div className="search"><Icon name="search" /><input type="text" value={q} onChange={(e) => setQ(e.target.value)} placeholder="ค้นรหัส ชื่อ หรือหมวด" aria-label="ค้นรหัสบัญชี" /></div>
        <div style={{ flex: 1 }} />
        <button className="btn primary sm" disabled={busy || !changed.length} onClick={save}>บันทึก{changed.length ? ` ${changed.length} รหัส` : ''}</button>
      </div>
      <div className="tw" style={{ maxHeight: 520, overflowY: 'auto' }}>
        <table className="t">
          <thead><tr><th>รหัส</th><th>ชื่อบัญชี</th><th>หมวด</th><th>ใครกรอก</th><th>เกณฑ์แดงเฉพาะรหัส (%)</th></tr></thead>
          <tbody>
            {shown.map((a) => {
              const c = cur(a);
              return (
                <tr key={a.code}>
                  <td className="mute">{a.code}</td>
                  <td>{a.name}</td>
                  <td className="mute small">{a.cat}</td>
                  <td>
                    <div className="seg">
                      <button className={c.filler === 'dept' ? 'on' : ''} onClick={() => edit(a.code, { filler: 'dept' })}>ฝ่ายกรอก</button>
                      <button className={c.filler === 'central' ? 'on' : ''} onClick={() => edit(a.code, { filler: 'central' })}>บัญชีกรอก</button>
                    </div>
                  </td>
                  <td><input type="text" inputMode="decimal" value={c.redPct} onChange={(e) => edit(a.code, { redPct: e.target.value })} placeholder="เกณฑ์กลาง" aria-label={'เกณฑ์เฉพาะรหัส ' + a.code} style={{ width: 110, height: 30 }} /></td>
                </tr>
              );
            })}
          </tbody>
        </table>
      </div>
    </div>
  );
}

function AdminSettings({ me, round, onRound }) {
  return (
    <div>
      <div className="head"><div className="grow"><h1>ตั้งค่า</h1><div className="mute small">งบประมาณปี {round.year} · ยอดอ้างอิงจาก {round.refSource || '–'}</div></div></div>
      <ThresholdCard round={round} onRound={onRound} />
      <ReferenceCard round={round} onChanged={onRound} />
      <UsersCard me={me} />
      <AccountsCard />
    </div>
  );
}
