/* global React, window */
const { useState: useDevState, useEffect: useDevEffect } = React;

// อุปกรณ์ Hikvision — register scanners, show webhook URL, test reach, sync clock.
// Devices push face-match events to /webhook/<code>.
function DevicePage({ role }) {
  const [devices, setDevices] = useDevState(window.DEVICES || []);
  const [editing, setEditing] = useDevState(null);
  const [testing, setTesting] = useDevState({});      // id → 'testing'|'ok'|'fail'
  const [usersFor, setUsersFor] = useDevState(null);  // เครื่องที่กำลังดูรายชื่อในเครื่อง
  const canEdit = role === 'admin';

  const reload = async () => {
    const r = await fetch('/api/devices', { credentials: 'include' });
    if (r.ok) { const d = await r.json(); setDevices(d); window.DEVICES = d; }
  };
  useDevEffect(() => { reload(); }, []);

  useDevEffect(() => { const t = setInterval(reload, 30000); return () => clearInterval(t); }, []);

  const test = async (dev) => {
    setTesting((t) => ({ ...t, [dev.id]: 'testing' }));
    try {
      const r = await fetch(`/api/devices/${dev.id}/test`, { method: 'POST', credentials: 'include' });
      const d = await r.json();
      setTesting((t) => ({ ...t, [dev.id]: d.ok ? 'ok' : 'fail' }));
      window.appToast(d.ok
        ? (d.via === 'push' ? `${devCode(dev)} ออนไลน์ — ${d.note}` : `${devCode(dev)} ออนไลน์ (ต่อตรงใน LAN)`)
        : `${devCode(dev)} ไม่ตอบ และไม่มีข้อมูลยิงเข้ามาใน 3 นาที (${d.error || d.statusCode || 'timeout'})`, { tone: d.ok ? 'success' : 'error', duration: 6000 });
      reload();
    } catch (e) {
      setTesting((t) => ({ ...t, [dev.id]: 'fail' }));
    }
  };

  const syncTime = async (dev) => {
    const r = await fetch(`/api/devices/${dev.id}/sync-time`, { method: 'POST', credentials: 'include' });
    const d = await r.json().catch(() => ({}));
    window.appToast(d.ok ? `ตั้งเวลา ${devCode(dev)} แล้ว` : 'ตั้งเวลาไม่สำเร็จ', { tone: d.ok ? 'success' : 'error' });
  };

  // Prefer the configured public URL so remote devices get a reachable webhook
  // address; fall back to the current origin for LAN use.
  const origin = (window.SETTINGS && window.SETTINGS.public_base_url) || window.location.origin;

  return (
    <div data-screen-label="Device">
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-end', gap: 12, flexWrap: 'wrap', marginBottom: 16 }}>
        <div>
          <h1 style={{ fontFamily: 'var(--font-display)', fontSize: 22, fontWeight: 700, margin: 0 }}>อุปกรณ์ Hikvision / Devices</h1>
          <div style={{ fontSize: 12.5, color: 'var(--ink-4)', marginTop: 4 }}>เครื่องสแกนใบหน้า · ตั้ง webhook ให้ส่ง event มาที่ระบบ</div>
        </div>
        {canEdit && (
          <button className="gv-btn ok sm" onClick={() => setEditing({})}>＋ เพิ่มเครื่อง</button>
        )}
      </div>

      {canEdit && <ScanIntervalControl/>}
      {canEdit && <CloudPanel/>}

      {devices.length === 0 ? (
        <div className="gv-card"><div className="gv-empty">— ยังไม่มีเครื่อง —</div></div>
      ) : (
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(340px, 1fr))', gap: 16 }}>
          {devices.map((d) => {
            const code = devCode(d);
            const webhookUrl = d.webhook_token ? `${origin}/webhook/t/${d.webhook_token}` : `${origin}/webhook/${(code.replace(/^DEV-/, '') || '01')}`;
            const seen = d.last_seen_ago_sec;
            const seenText = seen == null ? 'ยังไม่เคยมีข้อมูลเข้ามา' : (seen < 90 ? `ข้อมูลล่าสุด ${seen} วิ ก่อน` : seen < 3600 ? `ข้อมูลล่าสุด ${Math.round(seen / 60)} นาทีก่อน` : `ข้อมูลล่าสุด ${String(d.last_seen).slice(0, 16)}`);
            const st = testing[d.id];
            return (
              <div key={d.id} className="gv-card">
                <div className="gv-card-b">
                  <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 10 }}>
                    <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                      <span className="gv-chip tnum" style={{ background: 'var(--navy)', color: '#fff', fontWeight: 700 }}>{code}</span>
                      {d.online
                        ? <span className="gv-chip c-green" title={seenText}>● ออนไลน์</span>
                        : <span className="gv-chip c-gray" title={seenText}>○ ออฟไลน์</span>}
                    </div>
                    {canEdit && (
                      <div style={{ display: 'flex', gap: 4 }}>
                        <button className="gv-btn ghost sm" onClick={() => setEditing(d)}>แก้ไข</button>
                        <button className="gv-btn ghost sm" style={{ color: 'var(--coral-ink)' }}
                          onClick={async () => {
                            const ok = await window.appConfirm({ title: `ลบเครื่อง ${code}?`, danger: true, confirmText: 'ลบ' });
                            if (!ok) return;
                            await fetch(`/api/devices/${d.id}`, { method: 'DELETE', credentials: 'include' });
                            window.appToast('ลบแล้ว', { tone: 'success' }); reload();
                          }}>ลบ</button>
                      </div>
                    )}
                  </div>
                  <div style={{ fontWeight: 600, fontSize: 15, marginBottom: 2 }}>{d.name}</div>
                  <div className="tnum" style={{ fontSize: 12.5, color: 'var(--ink-4)', marginBottom: 12 }}>{d.ip}{d.location ? ` · ${d.location}` : ''} · <span style={{ color: d.online ? 'var(--mint-ink)' : 'var(--ink-5)' }}>{seenText}</span></div>

                  <div style={{ padding: 12, background: 'var(--surface-2)', borderRadius: 'var(--r-md)', marginBottom: 12 }}>
                    <div style={{ fontSize: 11, fontWeight: 600, color: 'var(--ink-4)', marginBottom: 4 }}>Webhook URL — ตั้งในเครื่องสแกน (HTTP Listening)</div>
                    <div className="tnum" style={{ fontSize: 11.5, wordBreak: 'break-all', color: 'var(--ink-3)' }}>{webhookUrl}</div>
                  </div>

                  <div style={{ display: 'flex', gap: 6 }}>
                    <button className="gv-btn no sm" onClick={() => test(d)} disabled={st === 'testing'}>
                      {st === 'testing' ? 'กำลังทดสอบ…' : '📶 ทดสอบ'}
                    </button>
                    {canEdit && <button className="gv-btn no sm" onClick={() => syncTime(d)}>🕑 ตั้งเวลา</button>}
                    {canEdit && <button className="gv-btn no sm" onClick={() => setUsersFor(d)}>👥 ผู้ใช้ในเครื่อง</button>}
                  </div>
                </div>
              </div>
            );
          })}
        </div>
      )}

      {editing && <DeviceModal device={editing} onClose={() => setEditing(null)} onSaved={() => { setEditing(null); reload(); }}/>}
      {usersFor && <DeviceUsersModal device={usersFor} onClose={() => setUsersFor(null)}/>}
    </div>
  );
}

function devCode(d) { return d.code || `DEV-${String(d.id).padStart(2, '0')}`; }

// รายชื่อที่ "อยู่ในตัวเครื่องสแกน" เทียบกับรายชื่อในระบบ — ลบคนที่ไม่มีในระบบออกจากเครื่องได้
function DeviceUsersModal({ device, onClose }) {
  const [data, setData] = useDevState(null);
  const [err, setErr] = useDevState('');
  const [busy, setBusy] = useDevState(false);
  const load = async () => {
    setErr(''); setData(null);
    const r = await fetch(`/api/devices/${device.id}/users`, { credentials: 'include' });
    const d = await r.json().catch(() => ({}));
    if (!r.ok) { setErr(d.error || 'อ่านไม่สำเร็จ'); return; }
    setData(d);
  };
  useDevEffect(() => { load(); }, [device.id]);
  const purge = async () => {
    const ok = await window.appConfirm({ title: `ลบ ${data.orphans.length} คนที่ไม่มีในระบบออกจากเครื่อง?`, message: data.orphans.map((u) => `${u.id} ${u.name || ''}`).join(', '), confirmText: 'ลบออกจากเครื่อง', danger: true });
    if (!ok) return;
    setBusy(true);
    const r = await fetch(`/api/devices/${device.id}/purge-orphans`, { method: 'POST', credentials: 'include' });
    const d = await r.json().catch(() => ({}));
    setBusy(false);
    window.appToast(r.ok ? `ลบออกจากเครื่องแล้ว ${d.removed} คน` : (d.error || 'ไม่สำเร็จ'), { tone: r.ok ? 'success' : 'error' });
    load();
  };
  return (
    <window.ModalShell title={`ผู้ใช้ในเครื่อง — ${devCode(device)}`} onClose={onClose} width={620}>
      {err && <div style={{ padding: '10px 14px', borderRadius: 10, background: 'var(--coral-soft)', color: 'var(--coral-ink)', fontSize: 13 }}>{err}</div>}
      {!data && !err && <div style={{ fontSize: 13 }}>กำลังอ่านจากเครื่อง…</div>}
      {data && (
        <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
          <div className="row" style={{ gap: 8, flexWrap: 'wrap' }}>
            <span className="gv-chip c-blue">ในเครื่อง {data.users.length} คน</span>
            <span className="gv-chip c-green">ตรงกับระบบ {data.users.length - data.orphans.length}</span>
            {data.orphans.length > 0 && <span className="gv-chip c-coral">ไม่มีในระบบ {data.orphans.length}</span>}
          </div>
          {data.users.length === 0 ? <div className="gv-empty">— เครื่องยังไม่มีผู้ใช้ —</div> : (
            <div style={{ maxHeight: 320, overflow: 'auto', border: '1px solid var(--line)', borderRadius: 10 }}>
              <table className="gv-tbl" style={{ fontSize: 13 }}>
                <thead><tr><th>รหัส</th><th>ชื่อในเครื่อง</th><th>รูปหน้า</th><th>ในระบบ</th></tr></thead>
                <tbody>{data.users.map((u) => (
                  <tr key={u.id} style={u.in_system ? null : { background: 'var(--coral-soft)' }}>
                    <td className="tnum">{u.id}</td><td>{u.name || '—'}</td>
                    <td>{u.numOfFace ? `✓ ${u.numOfFace}` : '—'}</td>
                    <td>{u.in_system ? <span className="gv-chip c-green">มี</span> : <span className="gv-chip c-coral">ไม่มี (ค้างในเครื่อง)</span>}</td>
                  </tr>
                ))}</tbody>
              </table>
            </div>
          )}
          <div className="row" style={{ justifyContent: 'flex-end', gap: 8 }}>
            <button className="gv-btn no sm" onClick={load} disabled={busy}>รีเฟรช</button>
            {data.orphans.length > 0 && <button className="gv-btn danger sm" onClick={purge} disabled={busy}>ลบคนที่ไม่มีในระบบออกจากเครื่อง ({data.orphans.length})</button>}
            <button className="gv-btn ok sm" onClick={onClose}>ปิด</button>
          </div>
        </div>
      )}
    </window.ModalShell>
  );
}

// Admin control for the minimum gap between two counted scans of the same
// person. Stops a quick re-scan from being logged as an immediate check-out.
function ScanIntervalControl() {
  const [sec, setSec] = useDevState('');
  const [saved, setSaved] = useDevState(false);
  useDevEffect(() => {
    fetch('/api/settings', { credentials: 'include' })
      .then((r) => (r.ok ? r.json() : null))
      .then((d) => { if (d) setSec(String((d.settings && d.settings.scan_interval_sec) || '60')); })
      .catch(() => setSec('60'));
  }, []);
  const save = async () => {
    const v = Math.max(0, parseInt(sec, 10) || 0);
    await fetch('/api/settings', { method: 'PUT', credentials: 'include', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ scan_interval_sec: String(v) }) });
    setSec(String(v)); setSaved(true); setTimeout(() => setSaved(false), 2000);
    window.appToast && window.appToast(`ตั้งช่วงเวลาสแกนซ้ำ = ${v} วินาที`, { tone: 'success' });
  };
  return (
    <div className="gv-card" style={{ marginBottom: 16 }}>
      <div className="gv-card-b" style={{ display: 'flex', alignItems: 'center', gap: 16, flexWrap: 'wrap' }}>
        <div style={{ minWidth: 0, flex: 1 }}>
          <div style={{ fontSize: 13.5, fontWeight: 600, color: 'var(--ink)' }}>ช่วงเวลาสแกนซ้ำ · Scan interval</div>
          <div style={{ fontSize: 12.5, color: 'var(--ink-4)', marginTop: 3 }}>สแกนซ้ำภายในเวลานี้จะไม่นับเป็นการลงเวลาใหม่ (กันเด้งเป็น "ออกงาน" ทันที)</div>
        </div>
        <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
          <input className="gv-input tnum" type="number" min="0" value={sec} onChange={(e) => setSec(e.target.value)} style={{ width: 90, textAlign: 'right' }}/>
          <span style={{ fontSize: 12.5, color: 'var(--ink-4)' }}>วินาที</span>
          <button className="gv-btn ok sm" onClick={save}>{saved ? '✓ บันทึกแล้ว' : 'บันทึก'}</button>
        </div>
      </div>
    </div>
  );
}

// เชื่อมต่อ Online — public webhook URL (for remote devices via tunnel) +
// Hik-Connect Cloud OpenAPI credentials (disabled until AppKey obtained).
function CloudPanel() {
  const [s, setS] = useDevState(null);
  const [open, setOpen] = useDevState(false);
  const [testing, setTesting] = useDevState('');
  const [cloudDevs, setCloudDevs] = useDevState(null);
  const [pulling, setPulling] = useDevState(false);
  const pull = async () => {
    setPulling(true);
    try {
      const r = await fetch('/api/cloud/devices', { credentials: 'include' });
      const d = await r.json();
      if (!r.ok || d.ok === false) throw new Error(d.error || 'ดึงไม่สำเร็จ');
      setCloudDevs(d);
      window.appToast(`ดึงได้ ${d.total} เครื่องจาก Hik-Connect`, { tone: 'success' });
    } catch (e) {
      window.appToast('ดึงเครื่องไม่สำเร็จ: ' + e.message, { tone: 'error' });
    } finally { setPulling(false); }
  };
  const load = async () => {
    const r = await fetch('/api/cloud/settings', { credentials: 'include' });
    if (r.ok) setS(await r.json());
  };
  useDevEffect(() => { load(); }, []);
  // Auto-pull the cloud device roster whenever the page opens (so it doesn't
  // vanish on navigation) and expand the panel so it's visible.
  useDevEffect(() => {
    if (s && s.configured && cloudDevs === null && !pulling) { setOpen(true); pull(); }
  }, [s]);
  const set = (k, v) => setS((p) => ({ ...p, [k]: v }));
  const save = async () => {
    await fetch('/api/cloud/settings', { method: 'PUT', credentials: 'include', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(s) });
    window.appToast('บันทึกแล้ว', { tone: 'success' }); load();
  };
  const test = async () => {
    setTesting('...');
    const r = await fetch('/api/cloud/test', { method: 'POST', credentials: 'include' }).then(x => x.json());
    setTesting(r.ok ? '✓ ' + r.message : '✗ ' + r.message);
    window.appToast(r.message, { tone: r.ok ? 'success' : 'error' });
  };
  if (!s) return null;

  return (
    <div className="gv-card" style={{ marginBottom: 16 }}>
      <button onClick={() => setOpen(!open)} style={{ width: '100%', display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '16px 20px', background: 'transparent', border: 0, cursor: 'pointer', fontFamily: 'inherit' }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
          <span style={{ fontSize: 16 }}>📶</span>
          <span style={{ fontWeight: 600, fontSize: 15, fontFamily: 'var(--font-display)', color: 'var(--ink)' }}>เชื่อมต่อ Online (เครื่องนอกวง LAN)</span>
          {s['cloud.enabled'] === '1' && <span className="gv-chip c-green">Cloud ON</span>}
        </div>
        <span style={{ color: 'var(--ink-4)' }}>{open ? '▾' : '▸'}</span>
      </button>

      {open && (
        <div style={{ padding: '0 20px 20px' }}>
          {/* Public URL — for remote devices via tunnel */}
          <div style={{ padding: 14, background: 'var(--surface-2)', borderRadius: 'var(--r-md)', marginBottom: 14 }}>
            <div style={{ fontSize: 12.5, fontWeight: 600, color: 'var(--ink-3)', marginBottom: 6 }}>วิธีที่ใช้ได้เลย: Public URL (ผ่าน Tunnel — ไม่ต้องมี API key)</div>
            <div style={{ fontSize: 12, color: 'var(--ink-4)', marginBottom: 8 }}>
              เปิด FaceInTime ออกเน็ตด้วย Cloudflare Tunnel แล้วใส่ URL สาธารณะที่นี่ — หน้าอุปกรณ์จะโชว์ webhook URL ที่เครื่อง remote ยิงเข้ามาได้
            </div>
            <div style={{ display: 'flex', gap: 8 }}>
              <input className="gv-input tnum" placeholder="https://faceintime.yourdomain.com" value={s.public_base_url || ''} onChange={(e) => set('public_base_url', e.target.value)} style={{ flex: 1 }}/>
              <button className="gv-btn ok sm" onClick={save}>บันทึก</button>
            </div>
          </div>

          {/* Cloud OpenAPI */}
          <div style={{ padding: 14, background: 'var(--surface-2)', borderRadius: 'var(--r-md)' }}>
            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 8 }}>
              <div style={{ fontSize: 12.5, fontWeight: 600, color: 'var(--ink-3)' }}>Hik-Connect Cloud OpenAPI</div>
              <label style={{ display: 'flex', alignItems: 'center', gap: 8, cursor: 'pointer' }}>
                <input type="checkbox" checked={s['cloud.enabled'] === '1'} onChange={(e) => set('cloud.enabled', e.target.checked ? '1' : '0')} style={{ width: 16, height: 16 }}/>
                <span style={{ fontSize: 12.5 }}>เปิดใช้ (poll)</span>
              </label>
            </div>
            <div style={{ fontSize: 11.5, marginBottom: 10, color: s.configured ? 'var(--mint-ink)' : 'var(--yellow-ink)' }}>
              {s.configured ? '● มี AppKey แล้ว — พร้อมใช้' : '● ยังไม่มี AppKey — สมัครที่ tpp.hikvision.com ก่อน (Download/Hik-Connect)'}
            </div>
            <div style={{ display: 'flex', gap: 8, marginBottom: 8 }}>
              <input className="gv-input tnum" placeholder="Base URL" value={s['cloud.base_url'] || ''} onChange={(e) => set('cloud.base_url', e.target.value)} style={{ flex: 1 }}/>
              <input className="gv-input" placeholder="Area ID" value={s['cloud.area_id'] || ''} onChange={(e) => set('cloud.area_id', e.target.value)} style={{ width: 120 }}/>
            </div>
            <div style={{ display: 'flex', gap: 8, marginBottom: 8 }}>
              <input className="gv-input tnum" placeholder="AppKey" value={s['cloud.app_key'] || ''} onChange={(e) => set('cloud.app_key', e.target.value)} style={{ flex: 1 }}/>
              <input className="gv-input tnum" type="password" placeholder="AppSecret" value={s['cloud.app_secret'] || ''} onChange={(e) => set('cloud.app_secret', e.target.value)} style={{ flex: 1 }}/>
            </div>
            <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap', alignItems: 'center' }}>
              <input className="gv-input" type="number" placeholder="วินาที/รอบ" value={s['cloud.poll_sec'] || ''} onChange={(e) => set('cloud.poll_sec', e.target.value)} style={{ width: 110 }}/>
              <button className="gv-btn no sm" onClick={test}>ทดสอบเชื่อมต่อ</button>
              <button className="gv-btn ok sm" onClick={save}>บันทึก</button>
              <button className="gv-btn no sm" onClick={pull} disabled={pulling}>{pulling ? 'กำลังดึง…' : '⬇ ดึงเครื่องจาก Cloud'}</button>
              {testing && <span style={{ fontSize: 12, alignSelf: 'center', color: testing.startsWith('✓') ? 'var(--mint-ink)' : 'var(--coral-ink)' }}>{testing}</span>}
            </div>

            {/* Live device roster pulled from HikCentral Connect */}
            {cloudDevs && (
              <div style={{ marginTop: 14, background: 'var(--surface)', border: '1px solid var(--line)', borderRadius: 'var(--r-md)', overflow: 'hidden' }}>
                <div style={{ display: 'grid', gridTemplateColumns: '1.6fr 1fr 1fr 90px', gap: 10, padding: '10px 14px', borderBottom: '1px solid var(--line)', background: 'var(--surface-2)', fontSize: 11, fontWeight: 600, color: 'var(--ink-4)' }}>
                  <span>ชื่อ / Name</span><span>Model</span><span>Serial</span><span style={{ textAlign: 'right' }}>Status</span>
                </div>
                {cloudDevs.devices.map((d) => (
                  <div key={d.cloud_id} style={{ display: 'grid', gridTemplateColumns: '1.6fr 1fr 1fr 90px', gap: 10, alignItems: 'center', padding: '11px 14px', borderBottom: '1px solid var(--line)' }}>
                    <span style={{ fontSize: 13.5, fontWeight: 500, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{d.name}</span>
                    <span className="tnum" style={{ fontSize: 12, color: 'var(--ink-4)' }}>{d.model}</span>
                    <span className="tnum" style={{ fontSize: 12, color: 'var(--ink-4)' }}>{d.serial || '—'}</span>
                    <span style={{ fontSize: 11.5, fontWeight: 600, textAlign: 'right', color: d.online ? 'var(--mint-ink)' : 'var(--coral-ink)' }}>{d.online ? '● Online' : '○ Offline'}</span>
                  </div>
                ))}
                <div style={{ padding: '9px 14px', fontSize: 11.5, color: 'var(--ink-4)' }}>{cloudDevs.total} เครื่อง · HikCentral Connect</div>
              </div>
            )}
          </div>
        </div>
      )}
    </div>
  );
}

function DeviceModal({ device, onClose, onSaved }) {
  const isNew = !device.id;
  const [name, setName] = useDevState(device.name || '');
  const [ip, setIp] = useDevState(device.ip || '');
  const [username, setUsername] = useDevState(device.username || 'admin');
  const [password, setPassword] = useDevState(device.password || '');
  const [code, setCode] = useDevState(device.code || '');
  const [location, setLocation] = useDevState(device.location || '');
  const [busy, setBusy] = useDevState(false);
  const [err, setErr] = useDevState('');

  const save = async () => {
    if (!name.trim() || !ip.trim()) { setErr('ต้องมีชื่อและ IP'); return; }
    setBusy(true); setErr('');
    try {
      const body = { name: name.trim(), ip: ip.trim(), username: username.trim(), password, code: code.trim(), location: location.trim() };
      const url = isNew ? '/api/devices' : `/api/devices/${device.id}`;
      const r = await fetch(url, { method: isNew ? 'POST' : 'PUT', credentials: 'include',
        headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) });
      const d = await r.json().catch(() => ({}));
      if (!r.ok) throw new Error(d.error || `error ${r.status}`);
      window.appToast(isNew ? 'เพิ่มเครื่องแล้ว' : 'บันทึกแล้ว', { tone: 'success' });
      onSaved();
    } catch (e) { setErr(e.message); } finally { setBusy(false); }
  };

  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(500px,100%)', maxHeight: '90vh', overflow: 'auto' }}>
        <div className="gv-card-h">
          <b>{isNew ? 'เพิ่มเครื่อง Hikvision' : 'แก้ไขเครื่อง'}</b>
          <button className="gv-btn no sm" onClick={onClose}>ปิด</button>
        </div>
        <div className="gv-card-b" style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
          <div style={{ display: 'flex', gap: 12 }}>
            <div className="gv-field" style={{ flex: 1, marginBottom: 0 }}>
              <label>ชื่อเครื่อง *</label>
              <input className="gv-input" value={name} autoFocus onChange={(e) => setName(e.target.value)} placeholder="ประตูหน้า"/>
            </div>
            <div className="gv-field" style={{ width: 110, marginBottom: 0 }}>
              <label>รหัส</label>
              <input className="gv-input" value={code} onChange={(e) => setCode(e.target.value)} placeholder="DEV-01"/>
            </div>
          </div>
          <div className="gv-field" style={{ marginBottom: 0 }}>
            <label>IP Address *</label>
            <input className="gv-input tnum" value={ip} onChange={(e) => setIp(e.target.value)} placeholder="192.168.1.100"/>
          </div>
          <div style={{ display: 'flex', gap: 12 }}>
            <div className="gv-field" style={{ flex: 1, marginBottom: 0 }}>
              <label>Username</label>
              <input className="gv-input" value={username} onChange={(e) => setUsername(e.target.value)}/>
            </div>
            <div className="gv-field" style={{ flex: 1, marginBottom: 0 }}>
              <label>Password</label>
              <input className="gv-input" type="password" value={password} onChange={(e) => setPassword(e.target.value)}/>
            </div>
          </div>
          <div className="gv-field" style={{ marginBottom: 0 }}>
            <label>ตำแหน่งที่ตั้ง</label>
            <input className="gv-input" value={location} onChange={(e) => setLocation(e.target.value)} placeholder="ชั้น 1 ประตูหน้า"/>
          </div>
          {err && <div style={{ padding: '10px 14px', borderRadius: 10, background: 'var(--coral-soft)', color: 'var(--coral-ink)', fontSize: 12.5, fontWeight: 600 }}>{err}</div>}
          <div style={{ display: 'flex', justifyContent: 'flex-end', gap: 8, marginTop: 4 }}>
            <button className="gv-btn no" onClick={onClose} disabled={busy}>ยกเลิก</button>
            <button className="gv-btn ok" onClick={save} disabled={busy}>{busy ? 'กำลังบันทึก…' : 'บันทึก'}</button>
          </div>
        </div>
      </div>
    </div>
  );
}

window.DevicePage = DevicePage;
