/* global React, window, document, navigator, fetch, location */
// ──────────────────────────────────────────────────────────────────────────
// จัดการลูกค้า (Tenants) — หน้าสำหรับ super admin ของ Face in Time.
// ลูกค้าแต่ละราย (อบต. / เทศบาล / บริษัท) มีฐานข้อมูลแยกจากกันคนละไฟล์
// หน้านี้ใช้ เพิ่ม / แก้ไข / ระงับ / ลบ ลูกค้า, จัดการผู้ใช้ของลูกค้า และ
// สลับเข้าไปดูระบบของลูกค้ารายนั้น (impersonate).
//
// ทุกอย่างเป็น global (ไม่มี import/export) เหมือนหน้าอื่น ๆ ในโปรเจกต์
// ชื่อที่ destructure จาก React ต้องไม่ชนกับไฟล์อื่น → ใช้ suffix "Tn"
// ──────────────────────────────────────────────────────────────────────────
const { useState: useStateTn, useEffect: useEffectTn, useMemo: useMemoTn } = React;

// ── ตารางค่าคงที่ ────────────────────────────────────────────────────────
const TN_PLANS = [
  { k: 'trial',      label: 'ทดลองใช้', chip: 'c-gray' },
  { k: 'basic',      label: 'พื้นฐาน',   chip: 'c-blue' },
  { k: 'pro',        label: 'มาตรฐาน',  chip: 'c-violet' },
  { k: 'enterprise', label: 'องค์กร',    chip: 'c-green' },
];
const TN_ROLES = [
  { k: 'admin',    label: 'ผู้ดูแลระบบ' },
  { k: 'hr',       label: 'ฝ่ายบุคคล' },
  { k: 'manager',  label: 'หัวหน้างาน' },
  { k: 'employee', label: 'พนักงาน' },
];
const TN_ORG_TYPES = [
  { k: 'gov',     label: 'อบต. / เทศบาล' },
  { k: 'company', label: 'บริษัท / เอกชน' },
];

function tnPlanMeta(plan) {
  const found = TN_PLANS.filter((p) => p.k === plan)[0];
  return found || { k: plan || '', label: plan || '—', chip: 'c-gray' };
}
function tnOrgLabel(t) {
  const found = TN_ORG_TYPES.filter((o) => o.k === t)[0];
  return found ? found.label : 'ไม่ระบุประเภท';
}
function tnRoleLabel(r) {
  const found = TN_ROLES.filter((x) => x.k === r)[0];
  return found ? found.label : (r || '—');
}

// ── helper เล็ก ๆ ────────────────────────────────────────────────────────
function tnToast(msg, tone) {
  if (window.appToast) window.appToast(msg, { tone: tone || 'info' });
}
function tnConfirm(opts) {
  const o = opts || {};
  if (window.appConfirm) return window.appConfirm(o);
  const text = (o.title || '') + (o.message ? '\n\n' + o.message : '');
  return Promise.resolve(window.confirm(text));
}
function tnRandPass(len) {
  const n = len || 10;
  const chars = 'abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789';
  let out = '';
  for (let i = 0; i < n; i++) out += chars.charAt(Math.floor(Math.random() * chars.length));
  return out;
}
function tnCopy(text) {
  const done = () => tnToast('คัดลอกแล้ว', 'success');
  try {
    if (navigator.clipboard && navigator.clipboard.writeText) {
      navigator.clipboard.writeText(text).then(done, () => tnCopyFallback(text));
      return;
    }
  } catch (e) { /* ignore — ใช้ fallback ด้านล่าง */ }
  tnCopyFallback(text);
}
function tnCopyFallback(text) {
  try {
    const ta = document.createElement('textarea');
    ta.value = text;
    ta.style.position = 'fixed';
    ta.style.top = '-2000px';
    document.body.appendChild(ta);
    ta.select();
    document.execCommand('copy');
    document.body.removeChild(ta);
    tnToast('คัดลอกแล้ว', 'success');
  } catch (e) {
    tnToast('คัดลอกไม่สำเร็จ — กรุณาคัดลอกด้วยตนเอง', 'error');
  }
}
// จำนวนวันคงเหลือถึงวันหมดอายุ (null = ไม่กำหนดวันหมดอายุ)
function tnDaysLeft(exp) {
  if (!exp) return null;
  const s = String(exp).slice(0, 10);
  const d = new Date(s + 'T00:00:00');
  if (Number.isNaN(d.getTime())) return null;
  const now = new Date();
  const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
  return Math.round((d.getTime() - today.getTime()) / 86400000);
}
function tnFmtDate(v) {
  if (!v) return '';
  const s = String(v).slice(0, 10);
  const parts = s.split('-');
  if (parts.length !== 3) return s;
  return parts[2] + '/' + parts[1] + '/' + parts[0];
}
function tnNum(v) {
  const n = Number(v);
  return Number.isFinite(n) ? n : 0;
}
function tnIsActive(t) {
  return !(t.is_active === 0 || t.is_active === false || t.is_active === '0');
}
async function tnJson(res) {
  try { return await res.json(); } catch (e) { return {}; }
}

// ── การ์ดสถิติด้านบน ─────────────────────────────────────────────────────
function TnStat({ tint, num, sub, label }) {
  return (
    <div className="gv-stat">
      <div style={{ display: 'flex', alignItems: 'center', gap: 11, marginBottom: 14 }}>
        <div className={'ic ' + (tint || 'i-blue')} style={{ margin: 0, flex: 'none' }}>
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.9" strokeLinecap="round" strokeLinejoin="round">
            <path d="M4 20V8l6-4 6 4v12M9 20v-5h4v5M20 20V11l-4-2.7" />
          </svg>
        </div>
        <div style={{ fontSize: 14.5, fontWeight: 600, color: 'var(--ink)', lineHeight: 1.25 }}>{label}</div>
      </div>
      <div className="num tnum">{num}{sub ? <small> {sub}</small> : null}</div>
    </div>
  );
}

// ── กล่องแจ้ง error แบบเดียวกับหน้าอื่น ──────────────────────────────────
function TnError({ text }) {
  if (!text) return null;
  return (
    <div style={{ padding: '10px 14px', borderRadius: 10, background: 'var(--coral-soft)', color: 'var(--coral-ink)', fontSize: 12.5, fontWeight: 600 }}>
      {text}
    </div>
  );
}

// ══════════════════════════════════════════════════════════════════════════
// หน้าหลัก
// ══════════════════════════════════════════════════════════════════════════
function TenantsPage(props) {
  const role = props && props.role;
  const [tenants, setTenants] = useStateTn([]);
  const [loading, setLoading] = useStateTn(true);
  const [loadErr, setLoadErr] = useStateTn('');
  const [search, setSearch] = useStateTn('');
  const [statusFilter, setStatusFilter] = useStateTn('all');
  const [adding, setAdding] = useStateTn(false);
  const [editing, setEditing] = useStateTn(null);   // tenant object
  const [usersOf, setUsersOf] = useStateTn(null);   // tenant object
  const [deleting, setDeleting] = useStateTn(null); // tenant object
  const [created, setCreated] = useStateTn(null);   // { tenant, username, password }
  const [busyId, setBusyId] = useStateTn(null);
  const [narrow, setNarrow] = useStateTn(typeof window !== 'undefined' && window.innerWidth <= 900);

  const load = async () => {
    setLoadErr('');
    try {
      const r = await fetch('/api/tenants', { credentials: 'include' });
      const d = await tnJson(r);
      if (!r.ok) throw new Error(d.error || ('โหลดข้อมูลไม่สำเร็จ (error ' + r.status + ')'));
      setTenants(Array.isArray(d.tenants) ? d.tenants : []);
    } catch (e) {
      setTenants([]);
      setLoadErr(e.message || 'โหลดข้อมูลไม่สำเร็จ');
    } finally {
      setLoading(false);
    }
  };

  useEffectTn(() => { load(); }, []);

  useEffectTn(() => {
    const onResize = () => setNarrow(window.innerWidth <= 900);
    window.addEventListener('resize', onResize);
    return () => window.removeEventListener('resize', onResize);
  }, []);

  // ── สถิติรวม ──
  const stats = useMemoTn(() => {
    let active = 0, suspended = 0, emps = 0, devs = 0;
    tenants.forEach((t) => {
      if (tnIsActive(t)) active++; else suspended++;
      const s = t.stats || {};
      emps += tnNum(s.employees);
      devs += tnNum(s.devices);
    });
    return { total: tenants.length, active: active, suspended: suspended, emps: emps, devs: devs };
  }, [tenants]);

  // ── กรอง ──
  const filtered = useMemoTn(() => {
    const q = search.trim().toLowerCase();
    return tenants.filter((t) => {
      if (statusFilter === 'active' && !tnIsActive(t)) return false;
      if (statusFilter === 'off' && tnIsActive(t)) return false;
      if (!q) return true;
      const hay = [t.name, t.code, t.contact_name, t.contact_phone, t.contact_email]
        .filter(Boolean).map((v) => String(v).toLowerCase());
      return hay.some((v) => v.indexOf(q) >= 0);
    });
  }, [tenants, search, statusFilter]);

  // ── การกระทำ ──
  const enterTenant = async (t) => {
    const ok = await tnConfirm({
      title: 'เข้าใช้งานระบบของ ' + (t.name || t.code) + '?',
      message: 'ระบบจะสลับไปทำงานบนฐานข้อมูลของหน่วยงานนี้ และรีโหลดหน้าจอ',
      confirmText: 'เข้าใช้งาน',
    });
    if (!ok) return;
    setBusyId(t.id);
    try {
      const r = await fetch('/api/tenants/' + encodeURIComponent(t.id) + '/enter', { method: 'POST', credentials: 'include' });
      const d = await tnJson(r);
      if (!r.ok) throw new Error(d.error || 'เข้าใช้งานไม่สำเร็จ');
      location.reload();
    } catch (e) {
      tnToast(e.message || 'เข้าใช้งานไม่สำเร็จ', 'error');
      setBusyId(null);
    }
  };

  const toggleActive = async (t) => {
    const on = tnIsActive(t);
    const ok = await tnConfirm({
      title: on ? ('ระงับการใช้งาน ' + (t.name || t.code) + '?') : ('เปิดใช้งาน ' + (t.name || t.code) + '?'),
      message: on ? 'ผู้ใช้ของหน่วยงานนี้จะเข้าระบบไม่ได้ชั่วคราว (ข้อมูลยังอยู่ครบ)' : 'ผู้ใช้ของหน่วยงานนี้จะกลับมาเข้าระบบได้ตามปกติ',
      confirmText: on ? 'ระงับการใช้งาน' : 'เปิดใช้งาน',
      danger: on,
    });
    if (!ok) return;
    setBusyId(t.id);
    try {
      const r = await fetch('/api/tenants/' + encodeURIComponent(t.id), {
        method: 'PUT', credentials: 'include',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ is_active: on ? 0 : 1 }),
      });
      const d = await tnJson(r);
      if (!r.ok) throw new Error(d.error || 'บันทึกไม่สำเร็จ');
      tnToast(on ? 'ระงับการใช้งานแล้ว' : 'เปิดใช้งานแล้ว', 'success');
      await load();
    } catch (e) {
      tnToast(e.message || 'บันทึกไม่สำเร็จ', 'error');
    } finally {
      setBusyId(null);
    }
  };

  // ── ส่วนหัว ──
  const head = (
    <div style={{ display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', gap: 12, flexWrap: 'wrap', marginBottom: 16 }}>
      <div>
        <h1 style={{ fontFamily: 'var(--font-display)', fontSize: 22, fontWeight: 700, margin: 0 }}>จัดการลูกค้า</h1>
        <div style={{ fontSize: 12.5, color: 'var(--ink-4)', marginTop: 4 }}>
          แต่ละหน่วยงานมีฐานข้อมูลแยกจากกันคนละไฟล์
        </div>
      </div>
      <button className="gv-btn ok" onClick={() => setAdding(true)}>＋ เพิ่มลูกค้า</button>
    </div>
  );

  return (
    <div data-screen-label="Tenants" data-role={role || ''}>
      {head}

      {/* แถวสถิติ */}
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit,minmax(170px,1fr))', gap: 14, marginBottom: 16 }}>
        <TnStat tint="i-blue"   num={stats.total}     sub="หน่วยงาน" label="ลูกค้าทั้งหมด" />
        <TnStat tint="i-green"  num={stats.active}    sub="หน่วยงาน" label="ใช้งานอยู่" />
        <TnStat tint="i-coral"  num={stats.suspended} sub="หน่วยงาน" label="ระงับการใช้งาน" />
        <TnStat tint="i-violet" num={stats.emps}      sub="คน"       label="พนักงานรวมทุกหน่วยงาน" />
        <TnStat tint="i-amber"  num={stats.devs}      sub="เครื่อง"   label="เครื่องสแกนรวม" />
      </div>

      {/* ตารางลูกค้า */}
      <div className="gv-card">
        <div className="gv-card-h" style={{ flexWrap: 'wrap', gap: 10 }}>
          <b>รายชื่อลูกค้า {loading ? '' : '(' + filtered.length + ')'}</b>
          <div style={{ display: 'flex', alignItems: 'center', gap: 10, flexWrap: 'wrap' }}>
            <input
              className="gv-input"
              style={{ width: narrow ? '100%' : 240, padding: '8px 12px', fontSize: 13 }}
              value={search}
              onChange={(e) => setSearch(e.target.value)}
              placeholder="ค้นหา ชื่อหน่วยงาน / รหัส"
            />
            <div className="gv-seg">
              <button className={statusFilter === 'all' ? 'on' : ''} onClick={() => setStatusFilter('all')}>ทั้งหมด</button>
              <button className={statusFilter === 'active' ? 'on' : ''} onClick={() => setStatusFilter('active')}>ใช้งาน</button>
              <button className={statusFilter === 'off' ? 'on' : ''} onClick={() => setStatusFilter('off')}>ระงับ</button>
            </div>
            <button className="gv-btn no sm" onClick={() => { setLoading(true); load(); }} title="โหลดข้อมูลใหม่">รีเฟรช</button>
          </div>
        </div>

        {loading ? (
          <div className="gv-empty">— กำลังโหลดข้อมูลลูกค้า —</div>
        ) : loadErr ? (
          <div className="gv-card-b" style={{ display: 'flex', flexDirection: 'column', gap: 12, alignItems: 'center' }}>
            <TnError text={loadErr} />
            <button className="gv-btn no sm" onClick={() => { setLoading(true); load(); }}>ลองใหม่อีกครั้ง</button>
          </div>
        ) : tenants.length === 0 ? (
          <div className="gv-card-b" style={{ textAlign: 'center', padding: '38px 20px' }}>
            <div style={{ fontSize: 15, fontWeight: 600, color: 'var(--ink)' }}>ยังไม่มีลูกค้าในระบบ</div>
            <div style={{ fontSize: 12.5, color: 'var(--ink-4)', marginTop: 6, marginBottom: 16 }}>
              เริ่มต้นด้วยการเพิ่มหน่วยงานแรก — ระบบจะสร้างฐานข้อมูลและบัญชีผู้ดูแลให้อัตโนมัติ
            </div>
            <button className="gv-btn ok" onClick={() => setAdding(true)}>＋ เพิ่มลูกค้ารายแรก</button>
          </div>
        ) : filtered.length === 0 ? (
          <div className="gv-empty">— ไม่พบลูกค้าที่ตรงกับเงื่อนไขการค้นหา —</div>
        ) : (
          <div style={{ overflowX: 'auto' }}>
            <table className="gv-tbl">
              <thead>
                <tr>
                  <th>หน่วยงาน</th>
                  <th>ผู้ติดต่อ</th>
                  <th>พนักงาน</th>
                  <th>เครื่องสแกน</th>
                  <th>สแกนวันนี้</th>
                  <th>แพ็กเกจ</th>
                  <th>วันหมดอายุ</th>
                  <th>สถานะ</th>
                  <th style={{ textAlign: 'right' }}>จัดการ</th>
                </tr>
              </thead>
              <tbody>
                {filtered.map((t) => {
                  const s = t.stats || {};
                  const on = tnIsActive(t);
                  const days = tnDaysLeft(t.expires_at);
                  const plan = tnPlanMeta(t.plan);
                  const busy = busyId === t.id;
                  const maxEmp = tnNum(t.max_employees);
                  const maxDev = tnNum(t.max_devices);
                  const nEmp = tnNum(s.employees);
                  const nDev = tnNum(s.devices);
                  let expColor = 'var(--ink)';
                  let expNote = '';
                  if (days !== null) {
                    if (days < 0) { expColor = 'var(--coral-ink)'; expNote = 'หมดอายุแล้ว'; }
                    else if (days <= 30) { expColor = 'var(--yellow-ink)'; expNote = 'เหลือ ' + days + ' วัน'; }
                  }
                  return (
                    <tr key={t.id}>
                      <td>
                        <div style={{ fontWeight: 600, fontSize: 14 }}>{t.name || '(ไม่มีชื่อ)'}</div>
                        <div style={{ fontSize: 11.5, color: 'var(--ink-5)', marginTop: 2 }}>
                          <span className="tnum">{t.code || '—'}</span> · {tnOrgLabel(t.org_type)}
                        </div>
                      </td>
                      <td>
                        {t.contact_name || t.contact_phone ? (
                          <div>
                            <div style={{ fontSize: 13 }}>{t.contact_name || '—'}</div>
                            {t.contact_phone ? <div className="tnum" style={{ fontSize: 11.5, color: 'var(--ink-5)', marginTop: 2 }}>{t.contact_phone}</div> : null}
                          </div>
                        ) : <span style={{ color: 'var(--ink-5)' }}>—</span>}
                      </td>
                      <td className="tnum">
                        <span style={{ fontWeight: 600, color: maxEmp > 0 && nEmp >= maxEmp ? 'var(--coral-ink)' : 'var(--ink)' }}>{nEmp}</span>
                        {maxEmp > 0 ? <span style={{ color: 'var(--ink-5)', fontSize: 12 }}> / {maxEmp}</span> : null}
                      </td>
                      <td className="tnum">
                        <span style={{ fontWeight: 600 }}>{nDev}</span>
                        {maxDev > 0 ? <span style={{ color: 'var(--ink-5)', fontSize: 12 }}> / {maxDev}</span> : null}
                      </td>
                      <td className="tnum">{tnNum(s.scans_today)}</td>
                      <td><span className={'gv-chip ' + plan.chip}>{plan.label}</span></td>
                      <td>
                        {days === null ? (
                          <span style={{ color: 'var(--ink-5)', fontSize: 12.5 }}>ไม่จำกัด</span>
                        ) : (
                          <div>
                            <div className="tnum" style={{ color: expColor, fontWeight: expNote ? 600 : 500, fontSize: 13 }}>{tnFmtDate(t.expires_at)}</div>
                            {expNote ? <div style={{ fontSize: 11, color: expColor, marginTop: 2 }}>{expNote}</div> : null}
                          </div>
                        )}
                      </td>
                      <td>
                        <span className={'gv-chip ' + (on ? 'c-green' : 'c-gray')}>{on ? 'ใช้งาน' : 'ระงับ'}</span>
                      </td>
                      <td>
                        <div style={{ display: 'flex', gap: 6, justifyContent: 'flex-end', flexWrap: 'wrap' }}>
                          <button className="gv-btn dark sm" disabled={busy} onClick={() => enterTenant(t)} title="สลับเข้าไปดูระบบของหน่วยงานนี้">เข้าใช้งาน</button>
                          <button className="gv-btn no sm" disabled={busy} onClick={() => setUsersOf(t)}>ผู้ใช้</button>
                          <button className="gv-btn no sm" disabled={busy} onClick={() => setEditing(t)}>แก้ไข</button>
                          <button className="gv-btn no sm" disabled={busy} onClick={() => toggleActive(t)}>{on ? 'ระงับ' : 'เปิดใช้'}</button>
                          <button className="gv-btn danger sm" disabled={busy} onClick={() => setDeleting(t)}>ลบ</button>
                        </div>
                      </td>
                    </tr>
                  );
                })}
              </tbody>
            </table>
          </div>
        )}
      </div>

      {adding && (
        <TenantFormModal
          mode="add"
          narrow={narrow}
          onClose={() => setAdding(false)}
          onSaved={(payload, tenant) => {
            setAdding(false);
            setCreated({ tenant: tenant || {}, username: payload.admin_username, password: payload.admin_password });
            load();
          }}
        />
      )}

      {editing && (
        <TenantFormModal
          mode="edit"
          narrow={narrow}
          tenant={editing}
          onClose={() => setEditing(null)}
          onSaved={() => { setEditing(null); tnToast('บันทึกข้อมูลลูกค้าแล้ว', 'success'); load(); }}
        />
      )}

      {usersOf && <TenantUsersModal tenant={usersOf} narrow={narrow} onClose={() => setUsersOf(null)} />}

      {deleting && (
        <TenantDeleteModal
          tenant={deleting}
          onClose={() => setDeleting(null)}
          onDone={() => { setDeleting(null); tnToast('ลบลูกค้าเรียบร้อยแล้ว', 'success'); load(); }}
        />
      )}

      {created && <TenantCreatedModal info={created} onClose={() => setCreated(null)} />}
    </div>
  );
}

// ══════════════════════════════════════════════════════════════════════════
// Modal: เพิ่ม / แก้ไข ลูกค้า
// ══════════════════════════════════════════════════════════════════════════
function TenantFormModal({ mode, tenant, narrow, onClose, onSaved }) {
  const isAdd = mode === 'add';
  const t = tenant || {};

  const initial = useMemoTn(() => ({
    name: t.name || '',
    code: t.code || '',
    org_type: t.org_type || 'gov',
    contact_name: t.contact_name || '',
    contact_phone: t.contact_phone || '',
    contact_email: t.contact_email || '',
    plan: t.plan || 'basic',
    max_employees: t.max_employees === 0 || t.max_employees ? String(t.max_employees) : '50',
    max_devices: t.max_devices === 0 || t.max_devices ? String(t.max_devices) : '2',
    expires_at: t.expires_at ? String(t.expires_at).slice(0, 10) : '',
    note: t.note || '',
    admin_username: 'admin',
    admin_password: tnRandPass(10),
    admin_display_name: 'ผู้ดูแลระบบ',
  }), []);

  const [f, setF] = useStateTn(initial);
  const [busy, setBusy] = useStateTn(false);
  const [err, setErr] = useStateTn('');
  const set = (k, v) => setF((prev) => {
    const next = Object.assign({}, prev);
    next[k] = v;
    return next;
  });

  const codeOk = /^[a-z0-9][a-z0-9-]*$/.test(f.code);

  const validate = () => {
    if (!f.name.trim()) return 'กรุณากรอกชื่อหน่วยงาน';
    if (isAdd) {
      if (!f.code.trim()) return 'กรุณากรอกรหัสหน่วยงาน (code)';
      if (!codeOk) return 'รหัสหน่วยงานใช้ได้เฉพาะ a-z, 0-9 และ - เท่านั้น และต้องขึ้นต้นด้วยตัวอักษรหรือตัวเลข';
      if (f.code.length < 2) return 'รหัสหน่วยงานสั้นเกินไป (อย่างน้อย 2 ตัวอักษร)';
      if (!f.admin_username.trim()) return 'กรุณากรอกชื่อผู้ใช้ของผู้ดูแล';
      if (!/^[A-Za-z0-9._-]{3,}$/.test(f.admin_username.trim())) return 'ชื่อผู้ใช้ต้องยาวอย่างน้อย 3 ตัว และใช้ได้เฉพาะ A-Z a-z 0-9 . _ -';
      if (!f.admin_password || f.admin_password.length < 6) return 'รหัสผ่านต้องยาวอย่างน้อย 6 ตัวอักษร';
    }
    if (f.contact_email && f.contact_email.indexOf('@') < 0) return 'รูปแบบอีเมลไม่ถูกต้อง';
    const me = Number(f.max_employees);
    if (f.max_employees !== '' && (!Number.isFinite(me) || me < 0)) return 'จำนวนพนักงานสูงสุดไม่ถูกต้อง';
    const md = Number(f.max_devices);
    if (f.max_devices !== '' && (!Number.isFinite(md) || md < 0)) return 'จำนวนเครื่องสูงสุดไม่ถูกต้อง';
    return '';
  };

  const save = async () => {
    const bad = validate();
    if (bad) { setErr(bad); return; }
    setBusy(true); setErr('');
    const payload = {
      name: f.name.trim(),
      org_type: f.org_type,
      contact_name: f.contact_name.trim(),
      contact_phone: f.contact_phone.trim(),
      contact_email: f.contact_email.trim(),
      plan: f.plan,
      max_employees: f.max_employees === '' ? 0 : Number(f.max_employees),
      max_devices: f.max_devices === '' ? 0 : Number(f.max_devices),
      expires_at: f.expires_at || null,
      note: f.note.trim(),
    };
    if (isAdd) {
      payload.code = f.code.trim();
      payload.admin_username = f.admin_username.trim();
      payload.admin_password = f.admin_password;
      payload.admin_display_name = f.admin_display_name.trim() || f.admin_username.trim();
    }
    try {
      const url = isAdd ? '/api/tenants' : '/api/tenants/' + encodeURIComponent(t.id);
      const r = await fetch(url, {
        method: isAdd ? 'POST' : 'PUT',
        credentials: 'include',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(payload),
      });
      const d = await tnJson(r);
      if (!r.ok) throw new Error(d.error || ('บันทึกไม่สำเร็จ (error ' + r.status + ')'));
      onSaved(payload, d.tenant);
    } catch (e) {
      setErr(e.message || 'บันทึกไม่สำเร็จ');
    } finally {
      setBusy(false);
    }
  };

  const cols = { display: 'grid', gridTemplateColumns: narrow ? '1fr' : '1fr 1fr', gap: 14 };

  return (
    <div onClick={onClose} style={{ position: 'fixed', inset: 0, zIndex: 200, background: 'rgba(15,23,42,.45)', display: 'grid', placeItems: 'center', padding: 20 }}>
      <div className="gv-card" onClick={(e) => e.stopPropagation()} style={{ width: 'min(880px,100%)', maxHeight: '92vh', overflow: 'auto' }}>
        <div className="gv-card-h">
          <b>{isAdd ? 'เพิ่มลูกค้าใหม่' : 'แก้ไขข้อมูลลูกค้า'}</b>
          <button className="gv-btn ghost sm" onClick={onClose}>ปิด</button>
        </div>
        <div className="gv-card-b" style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>

          <div className="gv-sec-title" style={{ marginTop: 0 }}>ข้อมูลหน่วยงาน</div>
          <div style={cols}>
            <div className="gv-field">
              <label>ชื่อหน่วยงาน *</label>
              <input className="gv-input" value={f.name} onChange={(e) => set('name', e.target.value)} placeholder="เช่น อบต.บ้านใหม่" />
            </div>
            <div className="gv-field">
              <label>รหัสหน่วยงาน (code) {isAdd ? '*' : ''}</label>
              <input
                className="gv-input"
                value={f.code}
                disabled={!isAdd}
                onChange={(e) => {
                  const code = e.target.value.toLowerCase().replace(/[^a-z0-9-]/g, '');
                  // ชื่อผู้ใช้ต้องไม่ซ้ำกันทั้งระบบ → เดาให้เป็น admin.<code>
                  // จนกว่าผู้ใช้จะพิมพ์แก้เอง
                  setF((prev) => {
                    const next = Object.assign({}, prev);
                    next.code = code;
                    const auto = 'admin.' + String(prev.code || '').replace(/-/g, '');
                    if (!prev.admin_username || prev.admin_username === 'admin' || prev.admin_username === auto) {
                      next.admin_username = code ? 'admin.' + code.replace(/-/g, '') : 'admin';
                    }
                    return next;
                  });
                }}
                placeholder="เช่น banmai หรือ abt-banmai"
                style={{ fontFamily: 'var(--font-mono)', opacity: isAdd ? 1 : 0.6 }}
              />
              <div style={{ fontSize: 11, color: f.code && !codeOk ? 'var(--coral-ink)' : 'var(--ink-5)' }}>
                {isAdd
                  ? 'ใช้ได้เฉพาะ a-z, 0-9 และขีดกลาง (-) · ใช้เป็นชื่อไฟล์ฐานข้อมูล เปลี่ยนภายหลังไม่ได้'
                  : 'รหัสหน่วยงานเปลี่ยนไม่ได้ เนื่องจากผูกกับไฟล์ฐานข้อมูล'}
              </div>
            </div>
            <div className="gv-field">
              <label>ประเภทหน่วยงาน</label>
              <select className="gv-select" value={f.org_type} onChange={(e) => set('org_type', e.target.value)}>
                {TN_ORG_TYPES.map((o) => <option key={o.k} value={o.k}>{o.label}</option>)}
              </select>
            </div>
            <div className="gv-field">
              <label>แพ็กเกจ</label>
              <select className="gv-select" value={f.plan} onChange={(e) => set('plan', e.target.value)}>
                {TN_PLANS.map((p) => <option key={p.k} value={p.k}>{p.label}</option>)}
              </select>
            </div>
            <div className="gv-field">
              <label>ชื่อผู้ติดต่อ</label>
              <input className="gv-input" value={f.contact_name} onChange={(e) => set('contact_name', e.target.value)} placeholder="เช่น คุณสมชาย ใจดี" />
            </div>
            <div className="gv-field">
              <label>เบอร์โทร</label>
              <input className="gv-input" value={f.contact_phone} onChange={(e) => set('contact_phone', e.target.value)} placeholder="08x-xxx-xxxx" />
            </div>
            <div className="gv-field">
              <label>อีเมล</label>
              <input className="gv-input" type="email" value={f.contact_email} onChange={(e) => set('contact_email', e.target.value)} placeholder="contact@example.go.th" />
            </div>
            <div className="gv-field">
              <label>วันหมดอายุ</label>
              <input className="gv-input" type="date" value={f.expires_at} onChange={(e) => set('expires_at', e.target.value)} />
              <div style={{ fontSize: 11, color: 'var(--ink-5)' }}>เว้นว่าง = ไม่จำกัดวันหมดอายุ</div>
            </div>
            <div className="gv-field">
              <label>จำนวนพนักงานสูงสุด</label>
              <input className="gv-input tnum" type="number" min="0" value={f.max_employees} onChange={(e) => set('max_employees', e.target.value)} placeholder="0 = ไม่จำกัด" />
            </div>
            <div className="gv-field">
              <label>จำนวนเครื่องสแกนสูงสุด</label>
              <input className="gv-input tnum" type="number" min="0" value={f.max_devices} onChange={(e) => set('max_devices', e.target.value)} placeholder="0 = ไม่จำกัด" />
            </div>
          </div>
          <div className="gv-field">
            <label>หมายเหตุ</label>
            <textarea className="gv-textarea" rows="2" value={f.note} onChange={(e) => set('note', e.target.value)} placeholder="(ไม่บังคับ) เช่น เงื่อนไขสัญญา ผู้ดูแลบัญชี" />
          </div>

          {isAdd && (
            <div>
              <div className="gv-sec-title">บัญชีผู้ดูแลของลูกค้า</div>
              <div style={{ fontSize: 12, color: 'var(--ink-5)', marginTop: -6, marginBottom: 12 }}>
                ระบบจะสร้างฐานข้อมูลใหม่ให้หน่วยงานนี้ พร้อมบัญชีผู้ดูแลด้านล่างสำหรับส่งให้ลูกค้าใช้เข้าระบบครั้งแรก
              </div>
              <div style={cols}>
                <div className="gv-field">
                  <label>ชื่อผู้ใช้ (username) *</label>
                  <input className="gv-input" value={f.admin_username} onChange={(e) => set('admin_username', e.target.value.replace(/\s/g, ''))} placeholder="admin" style={{ fontFamily: 'var(--font-mono)' }} />
                </div>
                <div className="gv-field">
                  <label>รหัสผ่าน *</label>
                  <div style={{ display: 'flex', gap: 8 }}>
                    <input className="gv-input" value={f.admin_password} onChange={(e) => set('admin_password', e.target.value)} style={{ fontFamily: 'var(--font-mono)' }} />
                    <button className="gv-btn no sm" type="button" style={{ flex: 'none' }} onClick={() => set('admin_password', tnRandPass(10))}>สุ่มรหัส</button>
                  </div>
                  <div style={{ fontSize: 11, color: 'var(--ink-5)' }}>อย่างน้อย 6 ตัวอักษร · จดไว้ก่อนกดบันทึก</div>
                </div>
                <div className="gv-field">
                  <label>ชื่อที่แสดง</label>
                  <input className="gv-input" value={f.admin_display_name} onChange={(e) => set('admin_display_name', e.target.value)} placeholder="ผู้ดูแลระบบ" />
                </div>
              </div>
            </div>
          )}

          <TnError text={err} />

          <div style={{ display: 'flex', justifyContent: 'flex-end', gap: 8, marginTop: 8 }}>
            <button className="gv-btn no" onClick={onClose} disabled={busy}>ยกเลิก</button>
            <button className="gv-btn ok" onClick={save} disabled={busy}>
              {busy ? (isAdd ? 'กำลังสร้างฐานข้อมูล…' : 'กำลังบันทึก…') : (isAdd ? 'สร้างลูกค้าใหม่' : 'บันทึกการแก้ไข')}
            </button>
          </div>
        </div>
      </div>
    </div>
  );
}

// ══════════════════════════════════════════════════════════════════════════
// Modal: สรุปข้อมูลหลังสร้างสำเร็จ (ส่งให้ลูกค้า)
// ══════════════════════════════════════════════════════════════════════════
function TenantCreatedModal({ info, onClose }) {
  const t = (info && info.tenant) || {};
  const url = location.origin;
  const lines = [
    'ระบบลงเวลา Face in Time — ' + (t.name || ''),
    'ลิงก์เข้าใช้งาน: ' + url,
    'ชื่อผู้ใช้: ' + info.username,
    'รหัสผ่าน: ' + info.password,
  ];
  const all = lines.join('\n');

  const Row = ({ label, value, mono }) => (
    <div style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '10px 0', borderBottom: '1px solid var(--line)' }}>
      <div style={{ fontSize: 12.5, color: 'var(--ink-4)', width: 110, flex: 'none' }}>{label}</div>
      <div style={{ flex: 1, minWidth: 0, fontSize: 14, fontWeight: 600, wordBreak: 'break-all', fontFamily: mono ? 'var(--font-mono)' : 'inherit' }}>{value}</div>
      <button className="gv-btn no sm" style={{ flex: 'none' }} onClick={() => tnCopy(String(value))}>คัดลอก</button>
    </div>
  );

  return (
    <div onClick={onClose} style={{ position: 'fixed', inset: 0, zIndex: 210, background: 'rgba(15,23,42,.45)', display: 'grid', placeItems: 'center', padding: 20 }}>
      <div className="gv-card" onClick={(e) => e.stopPropagation()} style={{ width: 'min(560px,100%)', maxHeight: '92vh', overflow: 'auto' }}>
        <div className="gv-card-h"><b>สร้างสำเร็จ ✓</b></div>
        <div className="gv-card-b">
          <div style={{ padding: '12px 14px', borderRadius: 12, background: 'var(--mint-soft)', color: 'var(--mint-ink)', fontSize: 12.5, fontWeight: 600, marginBottom: 14 }}>
            สร้างฐานข้อมูลและบัญชีผู้ดูแลของ {t.name || 'หน่วยงานใหม่'} เรียบร้อยแล้ว — ส่งข้อมูลด้านล่างนี้ให้ลูกค้า
            <div style={{ fontWeight: 500, marginTop: 4 }}>รหัสผ่านนี้แสดงเพียงครั้งเดียว กรุณาคัดลอกเก็บไว้</div>
          </div>

          <Row label="ลิงก์เข้าใช้งาน" value={url} mono />
          <Row label="รหัสหน่วยงาน" value={t.code || '—'} mono />
          <Row label="ชื่อผู้ใช้" value={info.username} mono />
          <Row label="รหัสผ่าน" value={info.password} mono />

          <div style={{ display: 'flex', justifyContent: 'flex-end', gap: 8, marginTop: 16 }}>
            <button className="gv-btn no" onClick={() => tnCopy(all)}>คัดลอกทั้งหมด</button>
            <button className="gv-btn ok" onClick={onClose}>เสร็จสิ้น</button>
          </div>
        </div>
      </div>
    </div>
  );
}

// ══════════════════════════════════════════════════════════════════════════
// Modal: ผู้ใช้ของหน่วยงาน
// ══════════════════════════════════════════════════════════════════════════
function TenantUsersModal({ tenant, narrow, onClose }) {
  const [users, setUsers] = useStateTn([]);
  const [loading, setLoading] = useStateTn(true);
  const [err, setErr] = useStateTn('');
  const [formErr, setFormErr] = useStateTn('');
  const [busy, setBusy] = useStateTn(false);
  const [showAdd, setShowAdd] = useStateTn(false);
  const [nu, setNu] = useStateTn({ username: '', password: tnRandPass(10), display_name: '', role: 'hr' });
  const [reset, setReset] = useStateTn(null); // { username, password }

  const setNuField = (k, v) => setNu((prev) => {
    const next = Object.assign({}, prev);
    next[k] = v;
    return next;
  });

  const base = '/api/tenants/' + encodeURIComponent(tenant.id) + '/users';

  const load = async () => {
    setErr('');
    try {
      const r = await fetch(base, { credentials: 'include' });
      const d = await tnJson(r);
      if (!r.ok) throw new Error(d.error || ('โหลดรายชื่อผู้ใช้ไม่สำเร็จ (error ' + r.status + ')'));
      setUsers(Array.isArray(d.users) ? d.users : []);
    } catch (e) {
      setUsers([]);
      setErr(e.message || 'โหลดรายชื่อผู้ใช้ไม่สำเร็จ');
    } finally {
      setLoading(false);
    }
  };

  useEffectTn(() => { load(); }, [tenant.id]);

  const addUser = async () => {
    if (!nu.username.trim()) { setFormErr('กรุณากรอกชื่อผู้ใช้'); return; }
    if (!/^[A-Za-z0-9._-]{3,}$/.test(nu.username.trim())) { setFormErr('ชื่อผู้ใช้ต้องยาวอย่างน้อย 3 ตัว และใช้ได้เฉพาะ A-Z a-z 0-9 . _ -'); return; }
    if (!nu.password || nu.password.length < 6) { setFormErr('รหัสผ่านต้องยาวอย่างน้อย 6 ตัวอักษร'); return; }
    setBusy(true); setFormErr('');
    try {
      const r = await fetch(base, {
        method: 'POST', credentials: 'include',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          username: nu.username.trim(),
          password: nu.password,
          display_name: nu.display_name.trim() || nu.username.trim(),
          role: nu.role,
        }),
      });
      const d = await tnJson(r);
      if (!r.ok) throw new Error(d.error || ('เพิ่มผู้ใช้ไม่สำเร็จ (error ' + r.status + ')'));
      setReset({ username: nu.username.trim(), password: nu.password });
      tnToast('เพิ่มผู้ใช้แล้ว', 'success');
      setNu({ username: '', password: tnRandPass(10), display_name: '', role: 'hr' });
      setShowAdd(false);
      await load();
    } catch (e) {
      setFormErr(e.message || 'เพิ่มผู้ใช้ไม่สำเร็จ');
    } finally {
      setBusy(false);
    }
  };

  const resetPassword = async (u) => {
    const gen = tnRandPass(10);
    let pwd = gen;
    if (window.appPrompt) {
      const v = await window.appPrompt({
        title: 'รีเซ็ตรหัสผ่านของ ' + u.username,
        message: 'กรอกรหัสผ่านใหม่ หรือใช้รหัสที่ระบบสุ่มให้ด้านล่าง',
        defaultValue: gen,
        placeholder: 'รหัสผ่านใหม่',
      });
      if (v === null || v === undefined) return;
      pwd = String(v);
    } else {
      const ok = window.confirm('รีเซ็ตรหัสผ่านของ ' + u.username + ' เป็น ' + gen + ' ?');
      if (!ok) return;
    }
    if (pwd.length < 6) { tnToast('รหัสผ่านต้องยาวอย่างน้อย 6 ตัวอักษร', 'error'); return; }
    setBusy(true);
    try {
      const r = await fetch(base + '/' + encodeURIComponent(u.id) + '/password', {
        method: 'PUT', credentials: 'include',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ new_password: pwd }),
      });
      const d = await tnJson(r);
      if (!r.ok) throw new Error(d.error || 'รีเซ็ตรหัสผ่านไม่สำเร็จ');
      setReset({ username: u.username, password: pwd });
      tnToast('รีเซ็ตรหัสผ่านแล้ว', 'success');
    } catch (e) {
      tnToast(e.message || 'รีเซ็ตรหัสผ่านไม่สำเร็จ', 'error');
    } finally {
      setBusy(false);
    }
  };

  const cols = { display: 'grid', gridTemplateColumns: narrow ? '1fr' : '1fr 1fr', gap: 14 };

  return (
    <div onClick={onClose} style={{ position: 'fixed', inset: 0, zIndex: 200, background: 'rgba(15,23,42,.45)', display: 'grid', placeItems: 'center', padding: 20 }}>
      <div className="gv-card" onClick={(e) => e.stopPropagation()} style={{ width: 'min(820px,100%)', maxHeight: '92vh', overflow: 'auto' }}>
        <div className="gv-card-h" style={{ flexWrap: 'wrap', gap: 10 }}>
          <div>
            <b>ผู้ใช้ของหน่วยงานนี้</b>
            <div style={{ fontSize: 11.5, color: 'var(--ink-5)', marginTop: 2 }}>
              {tenant.name || ''} · <span className="tnum">{tenant.code || ''}</span>
            </div>
          </div>
          <div style={{ display: 'flex', gap: 8 }}>
            <button className="gv-btn ok sm" onClick={() => { setShowAdd(!showAdd); setFormErr(''); }}>
              {showAdd ? 'ยกเลิกการเพิ่ม' : '＋ เพิ่มผู้ใช้'}
            </button>
            <button className="gv-btn ghost sm" onClick={onClose}>ปิด</button>
          </div>
        </div>

        <div className="gv-card-b" style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
          {reset && (
            <div style={{ padding: '12px 14px', borderRadius: 12, background: 'var(--mint-soft)', color: 'var(--mint-ink)', fontSize: 13, display: 'flex', alignItems: 'center', gap: 10, flexWrap: 'wrap' }}>
              <div style={{ flex: 1, minWidth: 200 }}>
                <b>ข้อมูลเข้าระบบล่าสุด</b>
                <div className="tnum" style={{ fontFamily: 'var(--font-mono)', marginTop: 4 }}>
                  {reset.username} / {reset.password}
                </div>
                <div style={{ fontSize: 11.5, fontWeight: 500, marginTop: 4 }}>แสดงครั้งเดียว — คัดลอกส่งให้ผู้ใช้ทันที</div>
              </div>
              <button className="gv-btn no sm" onClick={() => tnCopy(reset.username + ' / ' + reset.password)}>คัดลอก</button>
              <button className="gv-btn ghost sm" onClick={() => setReset(null)}>ปิด</button>
            </div>
          )}

          {showAdd && (
            <div style={{ background: 'var(--surface-2)', border: '1px solid var(--line)', borderRadius: 'var(--r-md)', padding: 14 }}>
              <div style={{ fontSize: 13, fontWeight: 600, marginBottom: 10 }}>เพิ่มผู้ใช้ใหม่</div>
              <div style={cols}>
                <div className="gv-field">
                  <label>ชื่อผู้ใช้ *</label>
                  <input className="gv-input" value={nu.username} onChange={(e) => setNuField('username', e.target.value.replace(/\s/g, ''))} placeholder="เช่น hr01" style={{ fontFamily: 'var(--font-mono)' }} />
                </div>
                <div className="gv-field">
                  <label>รหัสผ่าน *</label>
                  <div style={{ display: 'flex', gap: 8 }}>
                    <input className="gv-input" value={nu.password} onChange={(e) => setNuField('password', e.target.value)} style={{ fontFamily: 'var(--font-mono)' }} />
                    <button className="gv-btn no sm" type="button" style={{ flex: 'none' }} onClick={() => setNuField('password', tnRandPass(10))}>สุ่มรหัส</button>
                  </div>
                </div>
                <div className="gv-field">
                  <label>ชื่อที่แสดง</label>
                  <input className="gv-input" value={nu.display_name} onChange={(e) => setNuField('display_name', e.target.value)} placeholder="เช่น คุณสมหญิง" />
                </div>
                <div className="gv-field">
                  <label>สิทธิ์การใช้งาน</label>
                  <select className="gv-select" value={nu.role} onChange={(e) => setNuField('role', e.target.value)}>
                    {TN_ROLES.map((r) => <option key={r.k} value={r.k}>{r.label} ({r.k})</option>)}
                  </select>
                </div>
              </div>
              <TnError text={formErr} />
              <div style={{ display: 'flex', justifyContent: 'flex-end', gap: 8, marginTop: 10 }}>
                <button className="gv-btn no" onClick={() => { setShowAdd(false); setFormErr(''); }} disabled={busy}>ยกเลิก</button>
                <button className="gv-btn ok" onClick={addUser} disabled={busy}>{busy ? 'กำลังบันทึก…' : 'เพิ่มผู้ใช้'}</button>
              </div>
            </div>
          )}

          {loading ? (
            <div className="gv-empty">— กำลังโหลดรายชื่อผู้ใช้ —</div>
          ) : err ? (
            <div style={{ display: 'flex', flexDirection: 'column', gap: 12, alignItems: 'center' }}>
              <TnError text={err} />
              <button className="gv-btn no sm" onClick={() => { setLoading(true); load(); }}>ลองใหม่อีกครั้ง</button>
            </div>
          ) : users.length === 0 ? (
            <div className="gv-empty">— หน่วยงานนี้ยังไม่มีผู้ใช้ —</div>
          ) : (
            <div style={{ overflowX: 'auto' }}>
              <table className="gv-tbl">
                <thead>
                  <tr>
                    <th>ชื่อผู้ใช้</th>
                    <th>ชื่อที่แสดง</th>
                    <th>สิทธิ์</th>
                    <th>สถานะ</th>
                    <th>สร้างเมื่อ</th>
                    <th style={{ textAlign: 'right' }}>จัดการ</th>
                  </tr>
                </thead>
                <tbody>
                  {users.map((u) => (
                    <tr key={u.id}>
                      <td style={{ fontFamily: 'var(--font-mono)', fontWeight: 600 }}>{u.username}</td>
                      <td>{u.display_name || <span style={{ color: 'var(--ink-5)' }}>—</span>}</td>
                      <td><span className={'gv-chip ' + (u.role === 'admin' ? 'c-violet' : u.role === 'hr' ? 'c-blue' : 'c-gray')}>{tnRoleLabel(u.role)}</span></td>
                      <td><span className={'gv-chip ' + (tnIsActive(u) ? 'c-green' : 'c-gray')}>{tnIsActive(u) ? 'ใช้งาน' : 'ปิด'}</span></td>
                      <td className="tnum" style={{ fontSize: 12.5, color: 'var(--ink-4)' }}>{tnFmtDate(u.created_at)}</td>
                      <td style={{ textAlign: 'right' }}>
                        <button className="gv-btn no sm" disabled={busy} onClick={() => resetPassword(u)}>รีเซ็ตรหัสผ่าน</button>
                      </td>
                    </tr>
                  ))}
                </tbody>
              </table>
            </div>
          )}
        </div>
      </div>
    </div>
  );
}

// ══════════════════════════════════════════════════════════════════════════
// Modal: ลบลูกค้า (ต้องพิมพ์ code ยืนยัน)
// ══════════════════════════════════════════════════════════════════════════
function TenantDeleteModal({ tenant, onClose, onDone }) {
  const [typed, setTyped] = useStateTn('');
  const [purge, setPurge] = useStateTn(false);
  const [busy, setBusy] = useStateTn(false);
  const [err, setErr] = useStateTn('');
  const code = tenant.code || '';
  const match = typed.trim() === code && code !== '';
  const s = tenant.stats || {};

  const doDelete = async () => {
    if (!match) { setErr('กรุณาพิมพ์รหัสหน่วยงานให้ตรงกับ ' + code); return; }
    setBusy(true); setErr('');
    try {
      const url = '/api/tenants/' + encodeURIComponent(tenant.id) + (purge ? '?purge=1' : '');
      const r = await fetch(url, { method: 'DELETE', credentials: 'include' });
      const d = await tnJson(r);
      if (!r.ok) throw new Error(d.error || ('ลบไม่สำเร็จ (error ' + r.status + ')'));
      onDone();
    } catch (e) {
      setErr(e.message || 'ลบไม่สำเร็จ');
    } finally {
      setBusy(false);
    }
  };

  return (
    <div onClick={onClose} style={{ position: 'fixed', inset: 0, zIndex: 210, background: 'rgba(15,23,42,.45)', display: 'grid', placeItems: 'center', padding: 20 }}>
      <div className="gv-card" onClick={(e) => e.stopPropagation()} style={{ width: 'min(520px,100%)', maxHeight: '92vh', overflow: 'auto' }}>
        <div className="gv-card-h"><b style={{ color: 'var(--coral-ink)' }}>ลบลูกค้า</b></div>
        <div className="gv-card-b" style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
          <div style={{ padding: '12px 14px', borderRadius: 12, background: 'var(--coral-soft)', color: 'var(--coral-ink)', fontSize: 12.5, lineHeight: 1.55 }}>
            <b>คุณกำลังจะลบ {tenant.name || code}</b>
            <div style={{ marginTop: 6 }}>
              หน่วยงานนี้มีพนักงาน {tnNum(s.employees)} คน · เครื่องสแกน {tnNum(s.devices)} เครื่อง · ผู้ใช้ {tnNum(s.users)} บัญชี
            </div>
            <div style={{ marginTop: 6, fontWeight: 600 }}>การกระทำนี้ย้อนกลับไม่ได้</div>
          </div>

          <div className="gv-field" style={{ marginBottom: 0 }}>
            <label>พิมพ์รหัสหน่วยงาน <span style={{ fontFamily: 'var(--font-mono)', color: 'var(--coral-ink)' }}>{code}</span> เพื่อยืนยัน</label>
            <input
              className="gv-input"
              value={typed}
              onChange={(e) => setTyped(e.target.value)}
              placeholder={code}
              style={{ fontFamily: 'var(--font-mono)', borderColor: typed && !match ? 'var(--coral)' : undefined }}
            />
          </div>

          <label style={{ display: 'flex', alignItems: 'flex-start', gap: 10, cursor: 'pointer', fontSize: 13, lineHeight: 1.5 }}>
            <input type="checkbox" checked={purge} onChange={(e) => setPurge(e.target.checked)} style={{ marginTop: 3, flex: 'none' }} />
            <span>
              ลบไฟล์ฐานข้อมูลและรูปภาพทิ้งถาวร
              <span style={{ display: 'block', fontSize: 11.5, color: 'var(--ink-5)' }}>
                ถ้าไม่เลือก ระบบจะลบเฉพาะรายการลูกค้า แต่ยังเก็บไฟล์ฐานข้อมูลไว้ให้กู้คืนได้
              </span>
            </span>
          </label>

          <TnError text={err} />

          <div style={{ display: 'flex', justifyContent: 'flex-end', gap: 8 }}>
            <button className="gv-btn no" onClick={onClose} disabled={busy}>ยกเลิก</button>
            <button className="gv-btn danger" onClick={doDelete} disabled={busy || !match} style={{ opacity: match ? 1 : 0.5 }}>
              {busy ? 'กำลังลบ…' : (purge ? 'ลบถาวร' : 'ลบลูกค้า')}
            </button>
          </div>
        </div>
      </div>
    </div>
  );
}

window.TenantsPage = TenantsPage;
