/*
 * AccountApiKeys.jsx — Customer-facing API key management
 * v1.0.0 — 2026-05-20
 *
 * Wraps Path D Phase D2 endpoints (server.js v1.11.1+):
 *   - POST   /account/api-keys                 — generate
 *   - GET    /account/api-keys                 — list
 *   - DELETE /account/api-keys/:id             — revoke
 *   - POST   /account/api-keys/:id/regenerate  — rotate
 *
 * Surfaces in Shell nav under Account → API Keys for every customer-side role.
 * Super-admin + dev_admin see a "View all keys (SA)" toggle that queries with
 * ?all=true to inspect every key in the database. Customer roles only see
 * their own keys.
 *
 * Plain key string is shown ONCE — at generation + regeneration — inside a
 * dedicated "save this now" modal with copy-to-clipboard + saved-confirm gate.
 * It's never stored client-side after the modal closes; if user loses it
 * they must regenerate.
 *
 * Auth: window.WPSB.getToken() — defined in index.html bootstrap.
 *
 * Design Standards 7-point compliance: self-contained .jsx, window namespace,
 * IIFE, theme vars only, mobile responsive (480/768 breakpoints), WCAG 2.1 AA,
 * generic placeholders only (example.com).
 */
(function () {
  'use strict';

  const { useState, useEffect, useCallback } = React;
  const RAILWAY = (window.WPSB && window.WPSB.RAILWAY) || 'https://wpsitebeam-railway-api-production.up.railway.app';

  /* ── helpers ─────────────────────────────────────────────────────── */
  function fmtDate(iso) {
    if (!iso) return '\u2014';
    try {
      const d = new Date(iso);
      const now = Date.now();
      const diff = now - d.getTime();
      if (diff < 60_000)           return 'just now';
      if (diff < 3_600_000)        return Math.floor(diff / 60_000)   + 'm ago';
      if (diff < 86_400_000)       return Math.floor(diff / 3_600_000) + 'h ago';
      if (diff < 7 * 86_400_000)   return Math.floor(diff / 86_400_000) + 'd ago';
      return d.toLocaleDateString(undefined, { year: 'numeric', month: 'short', day: 'numeric' });
    } catch { return '\u2014'; }
  }
  function fmtInt(n) { return n == null || isNaN(n) ? '\u2014' : Number(n).toLocaleString(); }
  function genUuid() {
    if (typeof crypto !== 'undefined' && crypto.randomUUID) return crypto.randomUUID();
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
      const r = Math.random() * 16 | 0;
      return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
    });
  }
  function copyToClipboard(text) {
    if (navigator.clipboard && navigator.clipboard.writeText) {
      return navigator.clipboard.writeText(text);
    }
    // Fallback for older browsers
    const ta = document.createElement('textarea');
    ta.value = text; ta.style.position = 'fixed'; ta.style.opacity = '0';
    document.body.appendChild(ta); ta.select();
    try { document.execCommand('copy'); } catch {}
    document.body.removeChild(ta);
    return Promise.resolve();
  }

  /* ── network ─────────────────────────────────────────────────────── */
  function authHeaders() {
    const token = (window.WPSB && window.WPSB.getToken && window.WPSB.getToken()) || '';
    return { 'Authorization': 'Bearer ' + token, 'Content-Type': 'application/json' };
  }
  async function listKeys(showAll) {
    const url = `${RAILWAY}/account/api-keys` + (showAll ? '?all=true' : '');
    const r = await fetch(url, { headers: authHeaders() });
    const body = await r.json();
    if (!r.ok) throw new Error(body.error || ('HTTP ' + r.status));
    return body;
  }
  async function createKey(payload) {
    const r = await fetch(`${RAILWAY}/account/api-keys`, {
      method: 'POST', headers: authHeaders(), body: JSON.stringify(payload),
    });
    const body = await r.json();
    if (!r.ok) throw new Error(body.error || ('HTTP ' + r.status));
    return body;
  }
  async function revokeKey(id, reason) {
    const r = await fetch(`${RAILWAY}/account/api-keys/${id}`, {
      method: 'DELETE', headers: authHeaders(),
      body: JSON.stringify({ reason: reason || null }),
    });
    const body = await r.json();
    if (!r.ok) throw new Error(body.error || ('HTTP ' + r.status));
    return body;
  }
  async function regenerateKey(id) {
    const r = await fetch(`${RAILWAY}/account/api-keys/${id}/regenerate`, {
      method: 'POST', headers: authHeaders(), body: JSON.stringify({}),
    });
    const body = await r.json();
    if (!r.ok) throw new Error(body.error || ('HTTP ' + r.status));
    return body;
  }

  /* ── main component ──────────────────────────────────────────────── */
  function AccountApiKeys() {
    const [keys, setKeys]         = useState([]);
    const [loading, setLoading]   = useState(true);
    const [err, setErr]           = useState(null);
    const [showAll, setShowAll]   = useState(false);
    const [modal, setModal]       = useState(null); // { type, data? }
    const isSA = ['super_admin','dev_admin'].includes(
      (window.currentUser && window.currentUser.role) || ''
    );

    const refresh = useCallback(async () => {
      setLoading(true); setErr(null);
      try {
        const body = await listKeys(showAll && isSA);
        setKeys(body.keys || []);
      } catch (e) {
        setErr(e.message);
      } finally {
        setLoading(false);
      }
    }, [showAll, isSA]);

    useEffect(() => { refresh(); }, [refresh]);

    /* ── modal handlers ─────────────────────────────────────────────── */
    const onGenerateSubmit = async (form) => {
      try {
        // D3 hybrid: master keys send NEITHER site_url NOR install_id.
        // Per-install keys send both. mode defaults to 'live' for backward compat.
        const payload = { mode: form.mode || 'live' };
        if (form.key_type === 'master') {
          payload.key_type = 'master';
          if (form.label) payload.label = form.label;
        } else {
          payload.key_type   = 'per_install';
          payload.site_url   = form.site_url;
          payload.install_id = form.install_id || genUuid();
          if (form.label) payload.label = form.label;
        }
        const result = await createKey(payload);
        setModal({ type: 'show-key', data: result });
        refresh();
      } catch (e) {
        setModal({ type: 'generate', data: { error: e.message, ...(modal && modal.data) } });
      }
    };
    const onRevokeConfirm = async (id, reason) => {
      try {
        await revokeKey(id, reason);
        setModal(null);
        refresh();
      } catch (e) {
        setModal({ type: 'revoke-confirm', data: { id, reason, error: e.message } });
      }
    };
    const onRegenerateConfirm = async (id) => {
      try {
        const result = await regenerateKey(id);
        setModal({ type: 'show-key', data: result, regenerated: true });
        refresh();
      } catch (e) {
        setModal({ type: 'regen-confirm', data: { id, error: e.message } });
      }
    };

    /* ── render ──────────────────────────────────────────────────────── */
    return (
      <div style={{ maxWidth: 1280, margin: '0 auto' }}>
        <header style={{ marginBottom: 24 }}>
          <div style={{ display: 'flex', alignItems: 'flex-start', gap: 16, flexWrap: 'wrap', justifyContent: 'space-between' }}>
            <div style={{ flex: '1 1 280px', minWidth: 0 }}>
              <h1 style={{ margin: 0, fontSize: '1.5rem', color: 'var(--text)' }}>API Keys</h1>
              <p style={{ margin: '6px 0 0', color: 'var(--dim)', fontSize: '.92rem', lineHeight: 1.5 }}>
                Long-lived credentials for connecting WordPress installations to WPSiteBeam. Each key is scoped
                to a single site and can be revoked or rotated at any time.
              </p>
            </div>
            <button
              className="btn btn-primary"
              style={{ flexShrink: 0 }}
              onClick={() => setModal({ type: 'generate', data: {} })}
            >
              + Generate New Key
            </button>
          </div>

          {isSA && (
            <div style={{ marginTop: 16, padding: '10px 14px', background: 'var(--surface-2)',
                          border: '1px solid var(--border)', borderRadius: 8, display: 'flex',
                          alignItems: 'center', gap: 12, flexWrap: 'wrap' }}>
              <span style={{ fontSize: '.78rem', color: 'var(--dim)', fontWeight: 600 }}>SA</span>
              <label style={{ display: 'flex', alignItems: 'center', gap: 8, cursor: 'pointer', fontSize: '.85rem' }}>
                <input
                  type="checkbox"
                  checked={showAll}
                  onChange={(e) => setShowAll(e.target.checked)}
                />
                <span style={{ color: 'var(--text)' }}>View all keys (system-wide)</span>
              </label>
              <span style={{ fontSize: '.78rem', color: 'var(--dim)' }}>
                {showAll ? 'Showing all accounts\u2019 keys' : 'Showing only your keys'}
              </span>
            </div>
          )}
        </header>

        {err && (
          <div role="alert" style={{ padding: '12px 14px', background: 'var(--red-dim)',
                                     border: '1px solid var(--red)', borderRadius: 8, marginBottom: 16,
                                     color: 'var(--text)', fontSize: '.88rem' }}>
            <strong>Could not load API keys:</strong> {err}
          </div>
        )}

        {loading && <div style={{ padding: 40, textAlign: 'center', color: 'var(--dim)' }}>Loading\u2026</div>}

        {!loading && !err && keys.length === 0 && (
          <div style={{ padding: '60px 24px', textAlign: 'center', background: 'var(--surface-2)',
                        border: '1px dashed var(--border)', borderRadius: 12 }}>
            <div style={{ fontSize: '2.4rem', marginBottom: 12, opacity: 0.5 }}>🔐</div>
            <h2 style={{ margin: '0 0 8px', fontSize: '1.1rem', color: 'var(--text)' }}>No API keys yet</h2>
            <p style={{ margin: '0 auto 20px', color: 'var(--dim)', fontSize: '.92rem', maxWidth: 440, lineHeight: 1.5 }}>
              Generate your first key to connect a WordPress site. The key is shown only once at creation \u2014
              save it somewhere safe before closing the dialog.
            </p>
            <button className="btn btn-primary" onClick={() => setModal({ type: 'generate', data: {} })}>
              Generate Your First Key
            </button>
          </div>
        )}

        {!loading && !err && keys.length > 0 && (
          <KeysTable keys={keys} showAccount={showAll && isSA}
                     onRevoke={(k)    => setModal({ type: 'revoke-confirm', data: { id: k.id, label: k.label, site_url: k.site_url } })}
                     onRegenerate={(k) => setModal({ type: 'regen-confirm',   data: { id: k.id, label: k.label, site_url: k.site_url } })}/>
        )}

        {modal && modal.type === 'generate'        && <GenerateModal     data={modal.data} onSubmit={onGenerateSubmit}    onClose={() => setModal(null)} />}
        {modal && modal.type === 'show-key'        && <ShowKeyModal      data={modal.data} regenerated={modal.regenerated} onClose={() => setModal(null)} />}
        {modal && modal.type === 'revoke-confirm'  && <RevokeModal       data={modal.data} onConfirm={onRevokeConfirm}    onClose={() => setModal(null)} />}
        {modal && modal.type === 'regen-confirm'   && <RegenerateModal   data={modal.data} onConfirm={onRegenerateConfirm} onClose={() => setModal(null)} />}
      </div>
    );
  }

  /* ── KeysTable ───────────────────────────────────────────────────── */
  function KeysTable({ keys, showAccount, onRevoke, onRegenerate }) {
    return (
      <div style={{ background: 'var(--surface-2)', border: '1px solid var(--border)', borderRadius: 12, overflow: 'hidden' }}>
        <div style={{ overflowX: 'auto' }}>
          <table style={{ width: '100%', borderCollapse: 'collapse', fontSize: '.88rem' }}>
            <thead>
              <tr style={{ background: 'var(--surface-3)', borderBottom: '1px solid var(--border)' }}>
                {['Label', 'Type', 'Key', 'Site', ...(showAccount ? ['Account'] : []), 'Created', 'Last Used', 'Requests', 'Status', 'Actions'].map((h) => (
                  <th key={h} style={{ padding: '12px 14px', textAlign: 'left', fontWeight: 600, color: 'var(--text)', fontSize: '.78rem', textTransform: 'uppercase', letterSpacing: '.5px' }}>
                    {h}
                  </th>
                ))}
              </tr>
            </thead>
            <tbody>
              {keys.map((k) => {
                const isMaster = k.key_type === 'master';
                return (
                <tr key={k.id} style={{ borderBottom: '1px solid var(--border)' }}>
                  <td style={{ padding: '12px 14px', color: 'var(--text)', maxWidth: 200 }}>
                    {k.label || <span style={{ color: 'var(--dim)', fontStyle: 'italic' }}>Unnamed</span>}
                  </td>
                  <td style={{ padding: '12px 14px' }}>
                    {isMaster ? (
                      <span style={{ display: 'inline-block', padding: '2px 8px', background: 'var(--warn-dim)',
                                     color: 'var(--warn)', borderRadius: 10, fontSize: '.72rem', fontWeight: 600,
                                     border: '1px solid var(--warn)' }}
                            title="Master key — works on all your WordPress sites">
                        Master
                      </span>
                    ) : (
                      <span style={{ display: 'inline-block', padding: '2px 8px', background: 'var(--beam-dim)',
                                     color: 'var(--beam)', borderRadius: 10, fontSize: '.72rem', fontWeight: 600,
                                     border: '1px solid var(--beam)' }}
                            title="Per-install key — bound to one WordPress site">
                        Per-install
                      </span>
                    )}
                  </td>
                  <td style={{ padding: '12px 14px', color: 'var(--text)', fontFamily: 'monospace', fontSize: '.82rem' }}>
                    {k.key_prefix}\u2026
                  </td>
                  <td style={{ padding: '12px 14px', color: 'var(--dim)', fontSize: '.82rem', maxWidth: 240, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }} title={k.site_url || '(any site)'}>
                    {k.site_url || <span style={{ color: 'var(--dim)', fontStyle: 'italic' }}>(any site)</span>}
                  </td>
                  {showAccount && (
                    <td style={{ padding: '12px 14px', color: 'var(--dim)', fontSize: '.78rem', fontFamily: 'monospace' }} title={k.account_id}>
                      {k.account_id ? k.account_id.slice(0, 8) + '\u2026' : '\u2014'}
                    </td>
                  )}
                  <td style={{ padding: '12px 14px', color: 'var(--dim)', fontSize: '.82rem' }}>
                    {fmtDate(k.created_at)}
                  </td>
                  <td style={{ padding: '12px 14px', color: 'var(--dim)', fontSize: '.82rem' }}>
                    {fmtDate(k.last_used_at)}
                  </td>
                  <td style={{ padding: '12px 14px', color: 'var(--dim)', fontSize: '.82rem', textAlign: 'right' }}>
                    {fmtInt(k.request_count)}
                  </td>
                  <td style={{ padding: '12px 14px' }}>
                    {k.is_active ? (
                      <span style={{ display: 'inline-block', padding: '2px 8px', background: 'var(--green-dim)',
                                     color: 'var(--green)', borderRadius: 10, fontSize: '.72rem', fontWeight: 600 }}>
                        Active
                      </span>
                    ) : (
                      <span style={{ display: 'inline-block', padding: '2px 8px', background: 'var(--red-dim)',
                                     color: 'var(--red)', borderRadius: 10, fontSize: '.72rem', fontWeight: 600 }}
                            title={k.revoked_reason || 'Revoked'}>
                        Revoked
                      </span>
                    )}
                  </td>
                  <td style={{ padding: '12px 14px' }}>
                    {k.is_active && (
                      <div style={{ display: 'flex', gap: 6 }}>
                        <button className="btn btn-ghost btn-sm"
                                onClick={() => onRegenerate(k)}
                                title="Generate a new key for this entry, invalidating the current one">
                          Rotate
                        </button>
                        <button className="btn btn-ghost btn-sm"
                                style={{ color: 'var(--red)' }}
                                onClick={() => onRevoke(k)}
                                title="Revoke this key (cannot be undone)">
                          Revoke
                        </button>
                      </div>
                    )}
                  </td>
                </tr>
                );
              })}
            </tbody>
          </table>
        </div>
      </div>
    );
  }

  /* ── Modal shell ─────────────────────────────────────────────────── */
  function ModalShell({ title, children, onClose, danger }) {
    useEffect(() => {
      const onKey = (e) => { if (e.key === 'Escape') onClose(); };
      window.addEventListener('keydown', onKey);
      return () => window.removeEventListener('keydown', onKey);
    }, [onClose]);
    return (
      <div role="dialog" aria-modal="true" aria-label={title}
           style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,.55)', display: 'flex',
                    alignItems: 'center', justifyContent: 'center', zIndex: 1000, padding: 20 }}
           onClick={onClose}>
        <div onClick={(e) => e.stopPropagation()}
             style={{ background: 'var(--panel)', border: '1px solid ' + (danger ? 'var(--red)' : 'var(--border)'),
                      borderRadius: 12, maxWidth: 540, width: '100%', maxHeight: '90vh', overflowY: 'auto',
                      boxShadow: '0 12px 40px rgba(0,0,0,.4)' }}>
          <header style={{ padding: '18px 22px', borderBottom: '1px solid var(--border)',
                           display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
            <h2 style={{ margin: 0, fontSize: '1.05rem', color: danger ? 'var(--red)' : 'var(--text)' }}>{title}</h2>
            <button onClick={onClose} aria-label="Close" style={{ background: 'none', border: 'none', color: 'var(--dim)', fontSize: '1.4rem', lineHeight: 1, cursor: 'pointer', padding: 4 }}>×</button>
          </header>
          <div style={{ padding: '20px 22px' }}>{children}</div>
        </div>
      </div>
    );
  }

  /* ── Generate modal ──────────────────────────────────────────────── */
  function GenerateModal({ data, onSubmit, onClose }) {
    const [keyType,  setKeyType]   = useState(data.key_type || 'per_install');
    const [mode,     setMode]      = useState(data.mode     || 'live');
    const [siteUrl,  setSiteUrl]   = useState(data.site_url || '');
    const [label,    setLabel]     = useState(data.label    || '');
    const [submitting, setSubmitting] = useState(false);

    const submit = async (e) => {
      e.preventDefault();
      // Per-install requires site_url; Master does not.
      if (keyType === 'per_install' && !siteUrl.trim()) return;
      setSubmitting(true);
      await onSubmit({
        key_type:   keyType,
        mode:       mode,
        site_url:   keyType === 'per_install' ? siteUrl.trim() : null,
        label:      label.trim(),
        install_id: keyType === 'per_install' ? genUuid() : null,
      });
      setSubmitting(false);
    };

    /* radio-group helpers */
    const radioLabel = (active) => ({
      display: 'flex', flexDirection: 'column', gap: 4, flex: 1, minWidth: 0,
      padding: '12px 14px',
      background: active ? 'var(--beam-dim)' : 'var(--surface-2)',
      border: '1px solid ' + (active ? 'var(--beam)' : 'var(--border)'),
      borderRadius: 8, cursor: 'pointer',
      transition: 'background .12s, border-color .12s',
    });
    const radioInput = { position: 'absolute', opacity: 0, pointerEvents: 'none' };
    const radioTitle = (active) => ({
      fontSize: '.86rem', fontWeight: 600,
      color: active ? 'var(--beam)' : 'var(--text)',
    });
    const radioDesc = { fontSize: '.74rem', color: 'var(--dim)', lineHeight: 1.45 };

    return (
      <ModalShell title="Generate API Key" onClose={onClose}>
        <form onSubmit={submit}>
          {data.error && (
            <div role="alert" style={{ padding: '10px 12px', background: 'var(--red-dim)', border: '1px solid var(--red)',
                                       borderRadius: 6, marginBottom: 14, color: 'var(--text)', fontSize: '.85rem' }}>
              {data.error}
            </div>
          )}

          {/* ── Key type selector (hybrid per Q1 — locked 2026-05-20) ── */}
          <div style={{ marginBottom: 16 }}>
            <div style={{ fontSize: '.82rem', fontWeight: 600, color: 'var(--text)', marginBottom: 8 }}>
              Key type
            </div>
            <div style={{ display: 'flex', gap: 10 }}>
              <label style={radioLabel(keyType === 'per_install')}>
                <input type="radio" name="kt" value="per_install" style={radioInput}
                       checked={keyType === 'per_install'} onChange={() => setKeyType('per_install')}/>
                <span style={radioTitle(keyType === 'per_install')}>Per-install</span>
                <span style={radioDesc}>Bound to one WordPress site. Granular revocation. Recommended.</span>
              </label>
              <label style={radioLabel(keyType === 'master')}>
                <input type="radio" name="kt" value="master" style={radioInput}
                       checked={keyType === 'master'} onChange={() => setKeyType('master')}/>
                <span style={radioTitle(keyType === 'master')}>Master</span>
                <span style={radioDesc}>Works on all your WordPress sites. One active Master Key per user.</span>
              </label>
            </div>
          </div>

          {/* ── Mode selector (live vs test — Q3 locked 2026-05-20) ── */}
          <div style={{ marginBottom: 16 }}>
            <div style={{ fontSize: '.82rem', fontWeight: 600, color: 'var(--text)', marginBottom: 8 }}>
              Environment
            </div>
            <div style={{ display: 'flex', gap: 10 }}>
              <label style={radioLabel(mode === 'live')}>
                <input type="radio" name="mode" value="live" style={radioInput}
                       checked={mode === 'live'} onChange={() => setMode('live')}/>
                <span style={radioTitle(mode === 'live')}>Live</span>
                <span style={radioDesc}>Production key. Prefix <code>wpsb_live_</code>.</span>
              </label>
              <label style={radioLabel(mode === 'test')}>
                <input type="radio" name="mode" value="test" style={radioInput}
                       checked={mode === 'test'} onChange={() => setMode('test')}/>
                <span style={radioTitle(mode === 'test')}>Test</span>
                <span style={radioDesc}>Sandbox key. Prefix <code>wpsb_test_</code>.</span>
              </label>
            </div>
          </div>

          {/* ── Site URL (per-install only) ── */}
          {keyType === 'per_install' && (
            <label style={{ display: 'block', marginBottom: 14 }}>
              <div style={{ fontSize: '.82rem', fontWeight: 600, color: 'var(--text)', marginBottom: 6 }}>
                Site URL <span style={{ color: 'var(--red)' }}>*</span>
              </div>
              <input
                type="url" required autoFocus
                value={siteUrl}
                onChange={(e) => setSiteUrl(e.target.value)}
                placeholder="https://example.com"
                style={{ width: '100%', padding: '10px 12px', fontSize: '.92rem',
                         background: 'var(--surface-2)', border: '1px solid var(--border)',
                         borderRadius: 6, color: 'var(--text)', boxSizing: 'border-box' }}
              />
              <div style={{ fontSize: '.74rem', color: 'var(--dim)', marginTop: 4 }}>
                The WordPress site this key will be used on.
              </div>
            </label>
          )}

          {keyType === 'master' && (
            <div style={{ padding: '12px 14px', background: 'var(--warn-dim)', border: '1px solid var(--warn)',
                          borderRadius: 6, marginBottom: 14, color: 'var(--text)', fontSize: '.82rem', lineHeight: 1.55 }}>
              <strong style={{ color: 'var(--warn)' }}>Master Key:</strong> works on every WordPress site
              you connect. Higher convenience, broader blast radius if leaked. You can only have one
              active Master Key per user — generating a new one requires rotating or revoking the
              existing one first.
            </div>
          )}

          <label style={{ display: 'block', marginBottom: 20 }}>
            <div style={{ fontSize: '.82rem', fontWeight: 600, color: 'var(--text)', marginBottom: 6 }}>
              Label <span style={{ color: 'var(--dim)', fontWeight: 400 }}>(optional)</span>
            </div>
            <input
              type="text" maxLength={80}
              value={label}
              onChange={(e) => setLabel(e.target.value)}
              placeholder={keyType === 'master' ? 'e.g. Master key for agency' : 'e.g. Production site, Staging key'}
              style={{ width: '100%', padding: '10px 12px', fontSize: '.92rem',
                       background: 'var(--surface-2)', border: '1px solid var(--border)',
                       borderRadius: 6, color: 'var(--text)', boxSizing: 'border-box' }}
            />
            <div style={{ fontSize: '.74rem', color: 'var(--dim)', marginTop: 4 }}>
              For your reference only. Helps identify keys when managing several.
            </div>
          </label>
          <div style={{ display: 'flex', gap: 10, justifyContent: 'flex-end' }}>
            <button type="button" className="btn btn-ghost" onClick={onClose}>Cancel</button>
            <button type="submit" className="btn btn-primary" disabled={submitting || !siteUrl.trim()}>
              {submitting ? 'Generating\u2026' : 'Generate Key'}
            </button>
          </div>
        </form>
      </ModalShell>
    );
  }

  /* ── Show key modal (the "save it now" gate) ─────────────────────── */
  function ShowKeyModal({ data, regenerated, onClose }) {
    const [copied, setCopied]     = useState(false);
    const [confirmed, setConfirmed] = useState(false);
    const doCopy = async () => {
      await copyToClipboard(data.plain_key);
      setCopied(true);
      setTimeout(() => setCopied(false), 2400);
    };
    return (
      <ModalShell title={regenerated ? 'New API Key Generated' : 'API Key Created'} onClose={confirmed ? onClose : () => {}}>
        <div style={{ padding: '12px 14px', background: 'var(--warn-dim)', border: '1px solid var(--warn)',
                      borderRadius: 8, marginBottom: 16, color: 'var(--text)' }}>
          <strong style={{ display: 'block', marginBottom: 4, fontSize: '.9rem' }}>
            Save this key now \u2014 it will never be shown again.
          </strong>
          <div style={{ fontSize: '.82rem', color: 'var(--dim)', lineHeight: 1.5 }}>
            We store only a hash on the server. If you lose this key you\u2019ll need to rotate it from the keys list.
            {regenerated && ' The old key is no longer valid.'}
          </div>
        </div>
        <div style={{ marginBottom: 16 }}>
          <div style={{ fontSize: '.78rem', fontWeight: 600, color: 'var(--text)', marginBottom: 6 }}>
            Your API Key
          </div>
          <div style={{ display: 'flex', gap: 8 }}>
            <input
              type="text" readOnly value={data.plain_key}
              onFocus={(e) => e.target.select()}
              style={{ flex: 1, padding: '10px 12px', fontFamily: 'monospace', fontSize: '.82rem',
                       background: 'var(--surface-3)', border: '1px solid var(--border)',
                       borderRadius: 6, color: 'var(--text)' }}
            />
            <button type="button" className="btn btn-primary" onClick={doCopy} style={{ flexShrink: 0 }}>
              {copied ? '✓ Copied' : 'Copy'}
            </button>
          </div>
        </div>
        <div style={{ background: 'var(--surface-2)', border: '1px solid var(--border)',
                      borderRadius: 8, padding: '12px 14px', marginBottom: 18, fontSize: '.82rem', color: 'var(--dim)' }}>
          <strong style={{ color: 'var(--text)', display: 'block', marginBottom: 4 }}>How to use this key</strong>
          Paste it into the WPSiteBeam plugin on <code style={{ background: 'var(--surface-3)', padding: '1px 6px',
            borderRadius: 4, color: 'var(--text)' }}>{data.site_url}</code> when it asks to connect, or use it as a
          Bearer token in API requests.
        </div>
        <label style={{ display: 'flex', alignItems: 'flex-start', gap: 10, marginBottom: 18, cursor: 'pointer' }}>
          <input type="checkbox" checked={confirmed} onChange={(e) => setConfirmed(e.target.checked)}
                 style={{ marginTop: 3 }}/>
          <span style={{ fontSize: '.86rem', color: 'var(--text)' }}>
            I\u2019ve saved this key somewhere safe.
          </span>
        </label>
        <div style={{ display: 'flex', justifyContent: 'flex-end' }}>
          <button className="btn btn-primary" onClick={onClose} disabled={!confirmed}>Done</button>
        </div>
      </ModalShell>
    );
  }

  /* ── Revoke confirmation ─────────────────────────────────────────── */
  function RevokeModal({ data, onConfirm, onClose }) {
    const [reason, setReason] = useState('');
    const [submitting, setSubmitting] = useState(false);
    const submit = async (e) => {
      e.preventDefault();
      setSubmitting(true);
      await onConfirm(data.id, reason.trim() || null);
      setSubmitting(false);
    };
    return (
      <ModalShell title="Revoke API Key" onClose={onClose} danger>
        <form onSubmit={submit}>
          <p style={{ margin: '0 0 14px', color: 'var(--text)', fontSize: '.92rem', lineHeight: 1.5 }}>
            Revoking this key will stop all requests using it. <strong>This cannot be undone</strong> \u2014
            you\u2019ll need to generate a new key if you want to reconnect this site.
          </p>
          {data.label && (
            <div style={{ padding: '10px 12px', background: 'var(--surface-2)', border: '1px solid var(--border)',
                          borderRadius: 6, marginBottom: 14, fontSize: '.85rem' }}>
              <div style={{ color: 'var(--dim)', fontSize: '.74rem', marginBottom: 2 }}>Label</div>
              <div style={{ color: 'var(--text)', fontWeight: 600 }}>{data.label}</div>
              <div style={{ color: 'var(--dim)', fontSize: '.78rem', marginTop: 6 }}>{data.site_url}</div>
            </div>
          )}
          {data.error && (
            <div role="alert" style={{ padding: '10px 12px', background: 'var(--red-dim)', border: '1px solid var(--red)',
                                       borderRadius: 6, marginBottom: 14, color: 'var(--text)', fontSize: '.85rem' }}>
              {data.error}
            </div>
          )}
          <label style={{ display: 'block', marginBottom: 18 }}>
            <div style={{ fontSize: '.82rem', fontWeight: 600, color: 'var(--text)', marginBottom: 6 }}>
              Reason <span style={{ color: 'var(--dim)', fontWeight: 400 }}>(optional, recorded in audit log)</span>
            </div>
            <input
              type="text" maxLength={200}
              value={reason}
              onChange={(e) => setReason(e.target.value)}
              placeholder="e.g. Lost device, rotating after team change"
              style={{ width: '100%', padding: '10px 12px', fontSize: '.92rem',
                       background: 'var(--surface-2)', border: '1px solid var(--border)',
                       borderRadius: 6, color: 'var(--text)', boxSizing: 'border-box' }}
            />
          </label>
          <div style={{ display: 'flex', gap: 10, justifyContent: 'flex-end' }}>
            <button type="button" className="btn btn-ghost" onClick={onClose}>Cancel</button>
            <button type="submit" className="btn btn-danger" disabled={submitting}
                    style={{ background: 'var(--red)', color: '#fff', border: '1px solid var(--red)' }}>
              {submitting ? 'Revoking\u2026' : 'Revoke Key'}
            </button>
          </div>
        </form>
      </ModalShell>
    );
  }

  /* ── Regenerate confirmation ─────────────────────────────────────── */
  function RegenerateModal({ data, onConfirm, onClose }) {
    const [submitting, setSubmitting] = useState(false);
    const submit = async () => {
      setSubmitting(true);
      await onConfirm(data.id);
      setSubmitting(false);
    };
    return (
      <ModalShell title="Rotate API Key" onClose={onClose}>
        <p style={{ margin: '0 0 14px', color: 'var(--text)', fontSize: '.92rem', lineHeight: 1.5 }}>
          Rotating generates a fresh key and invalidates the current one immediately. Any plugin or integration
          using the current key will stop working until updated with the new key.
        </p>
        {data.label && (
          <div style={{ padding: '10px 12px', background: 'var(--surface-2)', border: '1px solid var(--border)',
                        borderRadius: 6, marginBottom: 14, fontSize: '.85rem' }}>
            <div style={{ color: 'var(--dim)', fontSize: '.74rem', marginBottom: 2 }}>Rotating</div>
            <div style={{ color: 'var(--text)', fontWeight: 600 }}>{data.label}</div>
            <div style={{ color: 'var(--dim)', fontSize: '.78rem', marginTop: 6 }}>{data.site_url}</div>
          </div>
        )}
        {data.error && (
          <div role="alert" style={{ padding: '10px 12px', background: 'var(--red-dim)', border: '1px solid var(--red)',
                                     borderRadius: 6, marginBottom: 14, color: 'var(--text)', fontSize: '.85rem' }}>
            {data.error}
          </div>
        )}
        <div style={{ display: 'flex', gap: 10, justifyContent: 'flex-end' }}>
          <button type="button" className="btn btn-ghost" onClick={onClose}>Cancel</button>
          <button type="button" className="btn btn-primary" onClick={submit} disabled={submitting}>
            {submitting ? 'Rotating\u2026' : 'Generate New Key'}
          </button>
        </div>
      </ModalShell>
    );
  }

  /* ── Export ──────────────────────────────────────────────────────── */
  window.AccountApiKeys = AccountApiKeys;
})();
