// Agents admin — Windows machines that send heartbeats to the central
// server (instead of being probed). Lives at /agents view; admin-only.
//
// Data flow:
//   GET    /api/admin/agents                       list
//   POST   /api/admin/agents                       create → returns enrollment_token (visible ONLY here)
//   PATCH  /api/admin/agents/{id}                  rename / change group / disable
//   POST   /api/admin/agents/{id}/pause            pause heartbeat alerting
//   POST   /api/admin/agents/{id}/resume
//   POST   /api/admin/agents/{id}/regenerate-enrollment
//   DELETE /api/admin/agents/{id}
//
// Hooks rules: every useState / useEffect declared above any early
// return — same pattern as users_admin.jsx and admin.jsx.

const AGENT_INPUT = {
  width: '100%', padding: '5px 9px', background: 'var(--surface)',
  border: '1px solid var(--border-2)', borderRadius: 2,
  color: 'var(--text)', fontFamily: 'var(--mono)', fontSize: 12, outline: 'none',
};

function EnrollmentTokenModal({ token, agent, expires, onClose, onCopied }) {
  const [copied, setCopied] = React.useState(false);
  if (!token) return null;
  const expiresIn = expires ? Math.max(0, Math.round((expires * 1000 - Date.now()) / 3600000)) : 24;

  async function copy() {
    try {
      await navigator.clipboard.writeText(token);
      setCopied(true);
      onCopied && onCopied();
      setTimeout(() => setCopied(false), 1800);
    } catch (_) {
      window.prompt('Copy this enrollment code:', token);
    }
  }

  return (
    <div style={{
      position:'fixed', inset:0, background:'rgba(5,8,12,0.78)', zIndex: 100,
      display:'flex', alignItems:'center', justifyContent:'center', padding: 20,
    }}>
      <div style={{
        background:'var(--bg-1)', border:'1px solid var(--border-2)',
        borderRadius: 4, padding: 22, maxWidth: 540, width: '100%',
        boxShadow:'0 20px 60px rgba(0,0,0,0.6)',
      }}>
        <div style={{fontSize: 11, letterSpacing:'0.1em', color:'var(--text-faint)', textTransform:'uppercase', marginBottom: 8}}>
          Enrollment code · {agent ? agent.label : ''}
        </div>
        <div style={{fontSize: 13, color:'var(--text-dim)', marginBottom: 14, lineHeight: 1.5}}>
          Този код се показва само сега. Копирай го и го въведи в pulse-agent.exe wizard-а на Windows машината.
          Изтича след ~{expiresIn}h.
        </div>
        <div style={{
          fontFamily:'var(--mono)', fontSize: 24, letterSpacing:'0.15em',
          padding:'14px 16px', background:'var(--surface)', border:'1px solid var(--border-2)',
          borderRadius: 3, textAlign:'center', color:'var(--accent)',
          userSelect:'all',
        }}>{token}</div>
        <div style={{display:'flex', gap: 8, marginTop: 14, justifyContent:'flex-end'}}>
          <a className="btn" href="/download/pulse-agent.v2.exe"
             style={{textDecoration:'none', padding:'5px 12px'}}>
            download pulse-agent.exe
          </a>
          <button className="btn" onClick={copy} style={{padding:'5px 12px'}}>
            {copied ? 'copied ✓' : 'copy code'}
          </button>
          <button className="btn" onClick={onClose} style={{padding:'5px 12px',
            color:'var(--accent)', borderColor:'var(--accent-dim)', background:'var(--accent-bg)'}}>
            done
          </button>
        </div>
      </div>
    </div>
  );
}

function AgentEditor({ agent, onClose, onSaved }) {
  const open = !!agent;
  const [label, setLabel] = React.useState('');
  const [group, setGroup] = React.useState('');
  const [busy, setBusy] = React.useState(false);
  const [err, setErr] = React.useState(null);

  React.useEffect(() => {
    if (!agent) return;
    setLabel(agent.label || '');
    setGroup(agent.group || 'Windows clients');
    setErr(null);
  }, [agent?.id]);

  if (!open) return <div className="drawer" />;

  async function save() {
    setBusy(true); setErr(null);
    try {
      const updated = await window.apiFetch(`/api/admin/agents/${agent.id}`, {
        method: 'PATCH',
        body: JSON.stringify({ label: label.trim(), group: group.trim() }),
      });
      onSaved(updated);
    } catch (e) { setErr(e.message || 'save failed'); }
    finally { setBusy(false); }
  }

  return (
    <div className="drawer open">
      <div className="drawer-head">
        <div style={{flex: 1, minWidth: 0}}>
          <div className="drawer-title">Edit agent</div>
          <div className="drawer-sub">{agent.id}</div>
        </div>
        <button className="close" onClick={onClose}>×</button>
      </div>
      <div className="drawer-body">
        <div className="tweak-row">
          <label>Label</label>
          <input style={AGENT_INPUT} value={label} onChange={e => setLabel(e.target.value)}
                 placeholder="Reception PC – CityDent Sofia" />
        </div>
        <div className="tweak-row">
          <label>Group</label>
          <input style={AGENT_INPUT} value={group} onChange={e => setGroup(e.target.value)}
                 placeholder="Windows clients" />
        </div>
        <div className="tweak-row">
          <label>Hostname</label>
          <div style={{color:'var(--text-mute)', fontSize: 11, fontFamily:'var(--mono)'}}>
            {agent.hostname || '—'}{agent.windowsUser ? ` · user ${agent.windowsUser}` : ''}
          </div>
        </div>
        <div className="tweak-row">
          <label>Hardware</label>
          <div style={{color:'var(--text-mute)', fontSize: 10, fontFamily:'var(--mono)', wordBreak:'break-all'}}>
            machine guid: {agent.machineGuid || '—'}<br />
            mac: {(agent.macAddresses || []).join(', ') || '—'}<br />
            agent v{agent.agentVersion || '—'}
          </div>
        </div>
        {err && <div style={{color:'var(--crit)', fontSize: 11}}>{err}</div>}
      </div>
      <div className="drawer-actions" style={{gridTemplateColumns:'1fr 1fr'}}>
        <button className="action-btn" onClick={onClose} disabled={busy}>cancel</button>
        <button className="action-btn" onClick={save} disabled={busy || !label.trim()}
                style={{color:'var(--accent)', borderColor:'var(--accent-dim)', background:'var(--accent-bg)'}}>
          {busy ? 'saving…' : 'save'}
        </button>
      </div>
    </div>
  );
}

function AddAgentForm({ onCreated }) {
  const [label, setLabel] = React.useState('');
  const [group, setGroup] = React.useState('Windows clients');
  const [busy, setBusy] = React.useState(false);
  const [err, setErr] = React.useState(null);

  async function submit(e) {
    e.preventDefault();
    setBusy(true); setErr(null);
    try {
      const res = await window.apiFetch('/api/admin/agents', {
        method: 'POST',
        body: JSON.stringify({ label: label.trim(), group: group.trim() || 'Windows clients' }),
      });
      onCreated(res);
      setLabel('');
    } catch (e) { setErr(e.message || 'create failed'); }
    finally { setBusy(false); }
  }

  return (
    <form onSubmit={submit} style={{padding: 12, borderBottom:'1px solid var(--border)', display:'grid',
                                    gridTemplateColumns:'2fr 1fr auto', gap: 8, alignItems:'end'}}>
      <div>
        <label style={{display:'block', fontSize: 9, letterSpacing:'0.1em', color:'var(--text-faint)',
                       textTransform:'uppercase', marginBottom: 4}}>Label</label>
        <input style={AGENT_INPUT} value={label} onChange={e => setLabel(e.target.value)}
               placeholder="Reception PC – CityDent Sofia" required />
      </div>
      <div>
        <label style={{display:'block', fontSize: 9, letterSpacing:'0.1em', color:'var(--text-faint)',
                       textTransform:'uppercase', marginBottom: 4}}>Group</label>
        <input style={AGENT_INPUT} value={group} onChange={e => setGroup(e.target.value)}
               placeholder="Windows clients" />
      </div>
      <button className="btn" type="submit" disabled={busy || !label.trim()}
              style={{padding:'6px 14px', color:'var(--accent)',
                      borderColor:'var(--accent-dim)', background:'var(--accent-bg)'}}>
        {busy ? '…' : 'new agent'}
      </button>
      {err && <div style={{gridColumn:'1 / -1', color:'var(--crit)', fontSize: 11}}>{err}</div>}
    </form>
  );
}

function AgentsAdmin() {
  const [agents, setAgents] = React.useState(null);
  const [editing, setEditing] = React.useState(null);
  const [confirmDelete, setConfirmDelete] = React.useState(null);
  const [tokenModal, setTokenModal] = React.useState(null); // { token, agent, expires }
  const [err, setErr] = React.useState(null);
  const [now, setNow] = React.useState(Date.now());

  async function load() {
    setErr(null);
    try {
      const data = await window.apiFetch('/api/admin/agents');
      setAgents(data.agents || []);
    } catch (e) { if (e.message !== 'unauthorized') setErr(e.message); }
  }

  React.useEffect(() => { load(); }, []);
  React.useEffect(() => {
    // Refresh list every 10s so liveness flips visible without manual reload.
    const t = setInterval(load, 10_000);
    // Tick state every second so the "last seen" relative timestamps move.
    const tick = setInterval(() => setNow(Date.now()), 1000);
    return () => { clearInterval(t); clearInterval(tick); };
  }, []);

  async function onCreated(res) {
    setTokenModal({ token: res.enrollmentToken, agent: res.agent, expires: res.enrollmentExpires });
    await load();
  }
  async function onSaved() { setEditing(null); await load(); }
  async function onTogglePause(a) {
    try {
      await window.apiFetch(`/api/admin/agents/${a.id}/${a.paused ? 'resume' : 'pause'}`, { method: 'POST' });
      await load();
    } catch (e) { setErr(e.message); }
  }
  async function onRegenerate(a) {
    if (!confirm(`Re-issue enrollment code for "${a.label || a.id}"?\n\nThe existing device key will be revoked — the running pulse-agent.exe will start failing with 401.`)) return;
    try {
      const res = await window.apiFetch(`/api/admin/agents/${a.id}/regenerate-enrollment`, { method: 'POST' });
      setTokenModal({ token: res.enrollmentToken, agent: res.agent, expires: res.enrollmentExpires });
      await load();
    } catch (e) { setErr(e.message); }
  }
  async function onDelete(a) {
    try {
      await window.apiFetch(`/api/admin/agents/${a.id}`, { method: 'DELETE' });
      await load();
    } catch (e) { setErr(e.message); }
  }

  const counts = React.useMemo(() => {
    if (!agents) return { ok: 0, warn: 0, crit: 0, unknown: 0, paused: 0 };
    const c = { ok: 0, warn: 0, crit: 0, unknown: 0, paused: 0 };
    for (const a of agents) {
      if (a.paused) { c.paused++; continue; }
      c[a.status] = (c[a.status] || 0) + 1;
    }
    return c;
  }, [agents]);

  return (
    <div style={{padding: 12, display:'flex', flexDirection:'column', gap: 12}}>
      <div className="card" style={{padding: 0}}>
        <div className="card-head" style={{gap: 8}}>
          <h3>Windows agents</h3>
          <span className="sub">
            {agents
              ? `${agents.length} · ${counts.ok} live · ${counts.warn} late · ${counts.crit} offline · ${counts.paused} paused`
              : 'loading…'}
          </span>
          <span className="spacer" style={{flex: 1}} />
          <a className="btn" href="/download/pulse-agent.v2.exe" style={{textDecoration:'none'}}>
            download .exe
          </a>
          <button className="btn" onClick={load}>reload</button>
        </div>
        <AddAgentForm onCreated={onCreated} />
        {err && <div style={{padding:'0 12px 8px', color:'var(--crit)', fontSize: 11}}>{err}</div>}
        <div style={{overflow:'auto'}}>
          <table className="tbl">
            <thead>
              <tr>
                <th style={{width: 12}}></th>
                <th>label</th>
                <th>hostname</th>
                <th>group</th>
                <th>last seen</th>
                <th>version</th>
                <th>status</th>
                <th style={{textAlign:'right'}}>actions</th>
              </tr>
            </thead>
            <tbody>
              {(agents || []).map(a => {
                const lastText = a.lastSeen
                  ? window.fmtRel(a.lastSeen)
                  : (a.enrollmentPending ? 'awaiting enrollment' : 'never');
                const statusLabel = a.paused ? 'paused'
                  : a.status === 'ok' ? 'live'
                  : a.status === 'warn' ? 'late'
                  : a.status === 'crit' ? 'offline'
                  : a.status === 'unknown' ? 'pending'
                  : a.status;
                return (
                  <tr key={a.id}>
                    <td><span className={`dot ${a.paused ? 'maint' : a.status}`} /></td>
                    <td style={{fontWeight: 500}}>{a.label || a.id}</td>
                    <td className="mute" style={{fontFamily:'var(--mono)'}}>{a.hostname || '—'}{a.windowsUser ? ` · ${a.windowsUser}` : ''}</td>
                    <td className="mute">{a.group}</td>
                    <td className="mute">{lastText}</td>
                    <td className="mute" style={{fontFamily:'var(--mono)', fontSize: 10}}>{a.agentVersion || '—'}</td>
                    <td><span className={`chip ${a.paused ? 'maint' : a.status}`}>{statusLabel}</span></td>
                    <td style={{textAlign:'right'}}>
                      <div style={{display:'inline-flex', gap: 4}}>
                        <button className="btn" onClick={() => setEditing(a)}>edit</button>
                        <button className="btn" onClick={() => onTogglePause(a)}>
                          {a.paused ? 'resume' : 'pause'}
                        </button>
                        <button className="btn" onClick={() => onRegenerate(a)}>re-enroll</button>
                        {confirmDelete === a.id ? (
                          <button className="btn" onClick={() => { setConfirmDelete(null); onDelete(a); }}
                                  style={{color:'var(--crit)', borderColor:'oklch(0.66 0.22 27 / 0.5)', background:'var(--crit-bg)'}}>
                            confirm?
                          </button>
                        ) : (
                          <button className="btn" onClick={() => setConfirmDelete(a.id)}>delete</button>
                        )}
                      </div>
                    </td>
                  </tr>
                );
              })}
              {(!agents || agents.length === 0) && (
                <tr><td colSpan="8" style={{padding: 24, textAlign:'center', color:'var(--text-mute)'}}>
                  {agents === null ? 'Loading…' : 'No agents yet. Click "new agent" to create your first enrollment code.'}
                </td></tr>
              )}
            </tbody>
          </table>
        </div>
      </div>

      <div className="card">
        <div className="card-head"><h3>How it works</h3></div>
        <ol style={{padding:'0 12px 12px 32px', lineHeight: 1.65, fontSize: 12, color:'var(--text-dim)'}}>
          <li>Click <strong>new agent</strong>. Enter a friendly label (e.g. "Reception PC – Sofia").</li>
          <li>Copy the one-time enrollment code shown in the modal — it's visible only once.</li>
          <li>Download <strong>pulse-agent.exe</strong> on the Windows machine and run it.</li>
          <li>The wizard asks for the server URL (default <code>https://pulse.sitnov.work</code>) and the enrollment code.</li>
          <li>Once enrolled, the agent runs in the system tray and pings the server every ~30 seconds.</li>
          <li>If a heartbeat is missed for 90 s the row goes <code>offline</code> and the regular alert pipeline fires.</li>
        </ol>
      </div>

      <AgentEditor agent={editing} onClose={() => setEditing(null)} onSaved={onSaved} />

      {tokenModal && (
        <EnrollmentTokenModal
          token={tokenModal.token}
          agent={tokenModal.agent}
          expires={tokenModal.expires}
          onClose={() => setTokenModal(null)}
        />
      )}
    </div>
  );
}

Object.assign(window, { AgentsAdmin });
