/* ═══════════════════════════════════════════════════════════════════
   UserManagement.jsx — SaaS Admin User Management Panel
   Session: May 3, 2026
   ═══════════════════════════════════════════════════════════════════
   Visible to: super_admin, admin, support roles
   Features:
     - User list with search + filters
     - Per-user lockout status + clear
     - Suspend / unsuspend (admin + SA only)
     - Auth event history per user
     - Active lockouts overview
   ═══════════════════════════════════════════════════════════════════ */

(function () {
  'use strict';
  const { useState, useEffect, useCallback, useRef } = React;

  const RAILWAY = window.WPSBD?.railwayUrl
    || 'https://wpsitebeam-railway-api-production.up.railway.app';

  /* ── API helpers ──────────────────────────────────────────────── */
  async function apiGet(path) {
    const token = window.currentToken || '';
    const r = await fetch(`${RAILWAY}${path}`, {
      headers: { 'Authorization': `Bearer ${token}` },
    });
    if (!r.ok) throw new Error((await r.json().catch(() => ({}))).error || `HTTP ${r.status}`);
    return r.json();
  }

  async function apiPost(path, body = {}) {
    const token = window.currentToken || '';
    const r = await fetch(`${RAILWAY}${path}`, {
      method: 'POST',
      headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' },
      body: JSON.stringify(body),
    });
    if (!r.ok) throw new Error((await r.json().catch(() => ({}))).error || `HTTP ${r.status}`);
    return r.json();
  }

  /* ── Role helpers ─────────────────────────────────────────────── */
  function currentRole() {
    return window.currentUser?.role || '';
  }
  function canSuspend() {
    return ['super_admin', 'admin', 'dev_admin', 'internal_partner'].includes(currentRole());
  }

  /* ── Status badge ─────────────────────────────────────────────── */
  function StatusBadge({ active, locked, suspended }) {
    if (!active || suspended) return (
      <span style={{ background: 'var(--red-dim)', color: 'var(--red)', border: '1px solid var(--red-dim)', borderRadius: 4, padding: '2px 7px', fontSize: '.72rem', fontWeight: 700 }}>
        SUSPENDED
      </span>
    );
    if (locked) return (
      <span style={{ background: 'var(--warn-dim)', color: 'var(--warn)', border: '1px solid var(--warn-dim)', borderRadius: 4, padding: '2px 7px', fontSize: '.72rem', fontWeight: 700 }}>
        LOCKED OUT
      </span>
    );
    return (
      <span style={{ background: 'var(--green-dim)', color: 'var(--green)', border: '1px solid var(--green-dim)', borderRadius: 4, padding: '2px 7px', fontSize: '.72rem', fontWeight: 700 }}>
        ACTIVE
      </span>
    );
  }

  /* ── User detail drawer ───────────────────────────────────────── */
  function UserDrawer({ userId, onClose, onAction }) {
    const [detail, setDetail] = useState(null);
    const [loading, setLoading] = useState(true);
    const [err, setErr] = useState(null);
    const [actionBusy, setActionBusy] = useState(null);
    const [actionMsg, setActionMsg] = useState(null);
    const [suspendReason, setSuspendReason] = useState('');
    const [showSuspendForm, setShowSuspendForm] = useState(false);

    useEffect(() => {
      setLoading(true); setErr(null);
      apiGet(`/admin/users/${userId}`)
        .then(setDetail)
        .catch(e => setErr(e.message))
        .finally(() => setLoading(false));
    }, [userId]);

    async function doAction(type, extra = {}) {
      setActionBusy(type); setActionMsg(null);
      try {
        const path = type === 'suspend'    ? `/admin/users/${userId}/suspend`
                   : type === 'unsuspend'  ? `/admin/users/${userId}/unsuspend`
                   : type === 'unlock'     ? `/admin/users/${userId}/unlock`
                   : null;
        const result = await apiPost(path, extra);
        setActionMsg({ ok: true, text: result.message || 'Done.' });
        // Refresh detail
        const fresh = await apiGet(`/admin/users/${userId}`);
        setDetail(fresh);
        onAction?.();
      } catch (e) {
        setActionMsg({ ok: false, text: e.message });
      } finally {
        setActionBusy(null);
        setShowSuspendForm(false);
      }
    }

    const u = detail?.user;
    const isSuspended = u && u.is_active === false;
    const isLocked = u?.login_locked;

    return (
      <div style={{
        position: 'fixed', inset: 0, zIndex: 1000,
        display: 'flex', justifyContent: 'flex-end',
      }}>
        {/* Backdrop */}
        <div onClick={onClose} style={{ position: 'absolute', inset: 0, background: 'rgba(0,0,0,.6)' }} />
        {/* Drawer */}
        <div style={{
          position: 'relative', width: 480, maxWidth: '95vw',
          background: 'var(--surface)', borderLeft: '1px solid var(--border)',
          overflowY: 'auto', padding: 24, display: 'flex', flexDirection: 'column', gap: 20,
        }}>
          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
            <h3 style={{ margin: 0, color: 'var(--text)', fontSize: '1rem' }}>User Detail</h3>
            <button onClick={onClose} style={{ background: 'none', border: 'none', color: 'var(--dim)', fontSize: '1.3rem', cursor: 'pointer' }}>✕</button>
          </div>

          {loading && <div style={{ color: 'var(--dim)', fontSize: '.85rem' }}>Loading…</div>}
          {err && <div style={{ color: 'var(--red)', fontSize: '.85rem' }}>{err}</div>}

          {u && (
            <>
              {/* Identity */}
              <div style={{ background: 'var(--surface2)', borderRadius: 8, padding: 16 }}>
                <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: 12 }}>
                  <div>
                    <div style={{ fontWeight: 700, color: 'var(--text)', fontSize: '.95rem' }}>{u.email}</div>
                    <div style={{ color: 'var(--dim)', fontSize: '.78rem', marginTop: 3 }}>
                      {u.role} · {u.plan} · joined {new Date(u.created_at).toLocaleDateString()}
                    </div>
                  </div>
                  <StatusBadge active={u.is_active} locked={isLocked} suspended={isSuspended} />
                </div>
                {u.accounts && (
                  <div style={{ fontSize: '.78rem', color: 'var(--dim)' }}>
                    Account: <span style={{ color: 'var(--text)' }}>{u.accounts.name || '—'}</span>
                    {' · '}Plan: <span style={{ color: 'var(--beam)' }}>{u.accounts.plan || u.plan}</span>
                  </div>
                )}
              </div>

              {/* Lockout status */}
              {isLocked && (
                <div style={{ background: 'var(--warn-dim)', border: '1px solid var(--warn-dim)', borderRadius: 8, padding: 14 }}>
                  <div style={{ fontWeight: 700, color: 'var(--warn)', fontSize: '.82rem', marginBottom: 6 }}>⚠ Login Locked Out</div>
                  <div style={{ color: 'var(--dim)', fontSize: '.78rem', lineHeight: 1.5 }}>
                    {u.login_attempts} failed attempt{u.login_attempts !== 1 ? 's' : ''}.
                    Locked until {new Date(u.login_locked_until).toLocaleString()}.
                  </div>
                  <button
                    onClick={() => doAction('unlock')}
                    disabled={actionBusy === 'unlock'}
                    style={{
                      marginTop: 10, padding: '6px 14px', background: 'var(--warn)', color: '#000',
                      border: 'none', borderRadius: 6, fontWeight: 700, fontSize: '.78rem',
                      cursor: 'pointer', opacity: actionBusy === 'unlock' ? .6 : 1,
                    }}>
                    {actionBusy === 'unlock' ? 'Clearing…' : '🔓 Clear Lockout'}
                  </button>
                </div>
              )}

              {/* IP lockouts */}
              {detail.ip_lockouts?.length > 0 && (
                <div style={{ background: 'var(--red-dim)', border: '1px solid var(--red-dim)', borderRadius: 8, padding: 14 }}>
                  <div style={{ fontWeight: 700, color: 'var(--red)', fontSize: '.82rem', marginBottom: 8 }}>🌐 IP Lockouts</div>
                  {detail.ip_lockouts.map(({ ip, locked_until }) => (
                    <div key={ip} style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 6 }}>
                      <span style={{ color: 'var(--dim)', fontSize: '.78rem', fontFamily: 'monospace' }}>{ip}</span>
                      <button
                        onClick={() => apiPost('/admin/lockouts/clear-ip', { ip }).then(() => doAction('unlock'))}
                        style={{ padding: '3px 10px', background: 'var(--red-dim)', color: 'var(--red)', border: '1px solid var(--red-dim)', borderRadius: 4, fontSize: '.72rem', cursor: 'pointer' }}>
                        Clear IP
                      </button>
                    </div>
                  ))}
                </div>
              )}

              {/* Suspend / Unsuspend */}
              {canSuspend() && (
                <div style={{ background: 'var(--surface2)', borderRadius: 8, padding: 14 }}>
                  <div style={{ fontWeight: 700, color: 'var(--text)', fontSize: '.82rem', marginBottom: 10 }}>Account Status</div>
                  {isSuspended ? (
                    <button
                      onClick={() => doAction('unsuspend')}
                      disabled={actionBusy === 'unsuspend'}
                      style={{
                        padding: '8px 16px', background: 'var(--green-dim)', color: 'var(--green)',
                        border: '1px solid var(--green-dim)', borderRadius: 6, fontWeight: 700,
                        fontSize: '.82rem', cursor: 'pointer', opacity: actionBusy === 'unsuspend' ? .6 : 1,
                      }}>
                      {actionBusy === 'unsuspend' ? 'Reactivating…' : '✅ Unsuspend Account'}
                    </button>
                  ) : (
                    <>
                      {!showSuspendForm ? (
                        <button
                          onClick={() => setShowSuspendForm(true)}
                          style={{
                            padding: '8px 16px', background: 'var(--red-dim)', color: 'var(--red)',
                            border: '1px solid var(--red-dim)', borderRadius: 6, fontWeight: 700,
                            fontSize: '.82rem', cursor: 'pointer',
                          }}>
                          🚫 Suspend Account
                        </button>
                      ) : (
                        <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
                          <input
                            type="text"
                            placeholder="Reason for suspension (shown to user)"
                            value={suspendReason}
                            onChange={e => setSuspendReason(e.target.value)}
                            style={{
                              padding: '8px 12px', background: 'var(--bg)', border: '1px solid var(--border)',
                              borderRadius: 6, color: 'var(--text)', fontSize: '.82rem',
                            }}
                          />
                          <div style={{ display: 'flex', gap: 8 }}>
                            <button
                              onClick={() => doAction('suspend', { reason: suspendReason || 'Suspended by admin' })}
                              disabled={actionBusy === 'suspend'}
                              style={{
                                flex: 1, padding: '7px 0', background: 'var(--red)', color: '#fff',
                                border: 'none', borderRadius: 6, fontWeight: 700, fontSize: '.8rem',
                                cursor: 'pointer', opacity: actionBusy === 'suspend' ? .6 : 1,
                              }}>
                              {actionBusy === 'suspend' ? 'Suspending…' : 'Confirm Suspend'}
                            </button>
                            <button onClick={() => setShowSuspendForm(false)} style={{
                              padding: '7px 14px', background: 'var(--surface)', border: '1px solid var(--border)',
                              borderRadius: 6, color: 'var(--dim)', fontSize: '.8rem', cursor: 'pointer',
                            }}>Cancel</button>
                          </div>
                        </div>
                      )}
                    </>
                  )}
                </div>
              )}

              {/* Action message */}
              {actionMsg && (
                <div style={{
                  padding: '10px 14px', borderRadius: 6, fontSize: '.82rem', fontWeight: 600,
                  background: actionMsg.ok ? 'var(--green-dim)' : 'var(--red-dim)',
                  color: actionMsg.ok ? 'var(--green)' : 'var(--red)',
                  border: `1px solid ${actionMsg.ok ? 'var(--green-dim)' : 'var(--red-dim)'}`,
                }}>
                  {actionMsg.ok ? '✓' : '✕'} {actionMsg.text}
                </div>
              )}

              {/* Auth event log */}
              {detail.auth_events?.length > 0 && (
                <div>
                  <div style={{ fontWeight: 700, color: 'var(--dim)', fontSize: '.75rem', textTransform: 'uppercase', letterSpacing: '.06em', marginBottom: 8 }}>
                    Auth History (last 20)
                  </div>
                  <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
                    {detail.auth_events.map((ev, i) => (
                      <div key={i} style={{
                        display: 'flex', justifyContent: 'space-between', alignItems: 'center',
                        padding: '6px 10px', background: 'var(--surface2)', borderRadius: 5,
                      }}>
                        <div style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
                          <span style={{
                            fontSize: '.68rem', padding: '2px 6px', borderRadius: 3, fontWeight: 700,
                            background: ev.event_type.includes('fail') || ev.event_type.includes('lock') || ev.event_type.includes('suspend') ? 'var(--red-dim)' : ev.event_type.includes('success') || ev.event_type.includes('unlock') || ev.event_type.includes('unsuspend') ? 'var(--green-dim)' : 'var(--beam-dim)',
                            color: ev.event_type.includes('fail') || ev.event_type.includes('lock') || ev.event_type.includes('suspend') ? 'var(--red)' : ev.event_type.includes('success') || ev.event_type.includes('unlock') || ev.event_type.includes('unsuspend') ? 'var(--green)' : '#00c8ef',
                          }}>
                            {ev.event_type}
                          </span>
                          {ev.ip && <span style={{ color: 'var(--dim)', fontSize: '.72rem', fontFamily: 'monospace' }}>{ev.ip}</span>}
                        </div>
                        <span style={{ color: 'var(--dim)', fontSize: '.72rem' }}>
                          {new Date(ev.created_at).toLocaleString()}
                        </span>
                      </div>
                    ))}
                  </div>
                </div>
              )}
            </>
          )}
        </div>
      </div>
    );
  }

  /* ── Main UserManagement component ───────────────────────────── */
  function UserManagement() {
    const [users, setUsers]         = useState([]);
    const [total, setTotal]         = useState(0);
    const [loading, setLoading]     = useState(true);
    const [err, setErr]             = useState(null);
    const [search, setSearch]       = useState('');
    const [filterActive, setFilterActive] = useState('');
    const [filterLocked, setFilterLocked] = useState(false);
    const [offset, setOffset]       = useState(0);
    const [selectedUser, setSelectedUser] = useState(null);
    const [lockouts, setLockouts]   = useState(null);
    const [tab, setTab]             = useState('users'); // users | lockouts
    const LIMIT = 25;
    const searchRef = useRef(null);

    const loadUsers = useCallback(async (newOffset = 0) => {
      setLoading(true); setErr(null);
      try {
        const params = new URLSearchParams({ limit: LIMIT, offset: newOffset });
        if (search) params.set('search', search);
        /* 2026-05-18 v1.10.2: align param name with server. Server endpoint
           expects status=active|suspended (not is_active=true|false). */
        if (filterActive === 'true')  params.set('status', 'active');
        if (filterActive === 'false') params.set('status', 'suspended');
        const data = await apiGet(`/admin/users?${params}`);
        let filtered = data.users || [];
        if (filterLocked) filtered = filtered.filter(u => u.login_locked);
        setUsers(filtered);
        setTotal(data.total || 0);
        setOffset(newOffset);
      } catch (e) {
        setErr(e.message);
      } finally {
        setLoading(false);
      }
    }, [search, filterActive, filterLocked]);

    const loadLockouts = useCallback(async () => {
      try {
        const data = await apiGet('/admin/lockouts');
        setLockouts(data);
      } catch (e) {
        /* 2026-05-18 v1.10.2: was `catch(e) {}` — silent swallow. Now
           surfaces error to the user so a 404/500 is visible instead
           of leaving the lockouts tab blank with no explanation. */
        console.warn('[UserManagement] loadLockouts failed:', e.message);
        setLockouts({ email_lockouts: [], ip_lockouts: [], _error: e.message });
      }
    }, []);

    useEffect(() => { loadUsers(0); }, [search, filterActive, filterLocked]);
    useEffect(() => { if (tab === 'lockouts') loadLockouts(); }, [tab]);

    const btn = (active, label, onClick) => (
      <button onClick={onClick} style={{
        padding: '5px 14px', borderRadius: 6, fontSize: '.78rem', fontWeight: 600, cursor: 'pointer',
        background: active ? 'var(--beam)' : 'var(--surface2)',
        color: active ? '#000' : 'var(--dim)',
        border: '1px solid ' + (active ? 'var(--beam)' : 'var(--border)'),
      }}>{label}</button>
    );

    /* Width fix 2026-05-20 — removed `padding: '24px 28px', maxWidth: 1100, margin: '0 auto'`
       to match Brand Profile / Account / Billing / AIUsage standard width.
       The .main grid container in shell.css handles overall width + padding. */
    return (
      <div>
        {/* Header */}
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 24 }}>
          <div>
            <h2 style={{ margin: 0, color: 'var(--text)', fontSize: '1.15rem' }}>User Management</h2>
            <p style={{ margin: '4px 0 0', color: 'var(--dim)', fontSize: '.82rem' }}>
              Unlock accounts, view auth history, and manage user access.
            </p>
          </div>
          <div style={{ display: 'flex', gap: 8 }}>
            {btn(tab === 'users', '👥 Users', () => setTab('users'))}
            {btn(tab === 'lockouts', '🔒 Active Lockouts', () => setTab('lockouts'))}
          </div>
        </div>

        {/* Users tab */}
        {tab === 'users' && (
          <>
            {/* Filters */}
            <div style={{ display: 'flex', gap: 10, marginBottom: 16, flexWrap: 'wrap' }}>
              <input
                ref={searchRef}
                type="text"
                placeholder="Search by email…"
                value={search}
                onChange={e => setSearch(e.target.value)}
                style={{
                  flex: 1, minWidth: 200, padding: '8px 12px',
                  background: 'var(--surface2)', border: '1px solid var(--border)',
                  borderRadius: 6, color: 'var(--text)', fontSize: '.84rem',
                }}
              />
              <select
                value={filterActive}
                onChange={e => setFilterActive(e.target.value)}
                style={{ padding: '8px 10px', background: 'var(--surface2)', border: '1px solid var(--border)', borderRadius: 6, color: 'var(--text)', fontSize: '.82rem' }}>
                <option value="">All Status</option>
                <option value="true">Active</option>
                <option value="false">Suspended</option>
              </select>
              <label style={{ display: 'flex', alignItems: 'center', gap: 6, color: 'var(--dim)', fontSize: '.82rem', cursor: 'pointer' }}>
                <input type="checkbox" checked={filterLocked} onChange={e => setFilterLocked(e.target.checked)} />
                Locked out only
              </label>
              <button onClick={() => loadUsers(0)} style={{ padding: '8px 14px', background: 'var(--surface2)', border: '1px solid var(--border)', borderRadius: 6, color: 'var(--dim)', fontSize: '.82rem', cursor: 'pointer' }}>
                ↻ Refresh
              </button>
            </div>

            {err && <div style={{ color: 'var(--red)', padding: 12, background: 'var(--red-dim)', borderRadius: 6, marginBottom: 12, fontSize: '.84rem' }}>{err}</div>}

            {/* Table */}
            <div style={{ background: 'var(--surface)', border: '1px solid var(--border)', borderRadius: 8, overflow: 'hidden' }}>
              <table style={{ width: '100%', borderCollapse: 'collapse', fontSize: '.82rem' }}>
                <thead>
                  <tr style={{ background: 'var(--surface2)' }}>
                    {['Email', 'Role', 'Plan', 'Status', 'Lockout', 'Joined', 'Actions'].map(h => (
                      <th key={h} style={{ padding: '10px 14px', textAlign: 'left', color: 'var(--dim)', fontWeight: 600, fontSize: '.74rem', textTransform: 'uppercase', letterSpacing: '.05em', whiteSpace: 'nowrap' }}>
                        {h}
                      </th>
                    ))}
                  </tr>
                </thead>
                <tbody>
                  {loading && (
                    <tr><td colSpan={7} style={{ padding: 24, color: 'var(--dim)', textAlign: 'center' }}>Loading…</td></tr>
                  )}
                  {!loading && users.length === 0 && (
                    <tr><td colSpan={7} style={{ padding: 24, color: 'var(--dim)', textAlign: 'center' }}>No users found.</td></tr>
                  )}
                  {!loading && users.map((u, i) => (
                    <tr key={u.id} style={{ borderTop: '1px solid var(--border)', background: i % 2 === 0 ? 'transparent' : 'rgba(255,255,255,.01)' }}>
                      <td style={{ padding: '10px 14px', color: 'var(--text)', maxWidth: 220, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{u.email}</td>
                      <td style={{ padding: '10px 14px', color: 'var(--dim)' }}>{u.role}</td>
                      <td style={{ padding: '10px 14px', color: 'var(--beam)', fontWeight: 600 }}>{u.plan}</td>
                      <td style={{ padding: '10px 14px' }}>
                        <StatusBadge active={u.is_active} locked={u.login_locked} suspended={u.is_active === false} />
                      </td>
                      <td style={{ padding: '10px 14px', color: u.login_locked ? 'var(--warn)' : 'var(--dim)' }}>
                        {u.login_locked ? `🔒 ${u.login_attempts} fail${u.login_attempts !== 1 ? 's' : ''}` : '—'}
                      </td>
                      <td style={{ padding: '10px 14px', color: 'var(--dim)', whiteSpace: 'nowrap' }}>
                        {new Date(u.created_at).toLocaleDateString()}
                      </td>
                      <td style={{ padding: '10px 14px' }}>
                        <button
                          onClick={() => setSelectedUser(u.id)}
                          style={{ padding: '4px 12px', background: 'var(--surface2)', border: '1px solid var(--border)', borderRadius: 5, color: 'var(--text)', fontSize: '.76rem', cursor: 'pointer' }}>
                          View →
                        </button>
                      </td>
                    </tr>
                  ))}
                </tbody>
              </table>
            </div>

            {/* Pagination */}
            {total > LIMIT && (
              <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginTop: 14 }}>
                <span style={{ color: 'var(--dim)', fontSize: '.78rem' }}>
                  Showing {offset + 1}–{Math.min(offset + LIMIT, total)} of {total} users
                </span>
                <div style={{ display: 'flex', gap: 8 }}>
                  <button onClick={() => loadUsers(Math.max(0, offset - LIMIT))} disabled={offset === 0} style={{ padding: '6px 14px', background: 'var(--surface2)', border: '1px solid var(--border)', borderRadius: 5, color: offset === 0 ? 'var(--dim)' : 'var(--text)', cursor: offset === 0 ? 'default' : 'pointer', fontSize: '.78rem' }}>← Prev</button>
                  <button onClick={() => loadUsers(offset + LIMIT)} disabled={offset + LIMIT >= total} style={{ padding: '6px 14px', background: 'var(--surface2)', border: '1px solid var(--border)', borderRadius: 5, color: offset + LIMIT >= total ? 'var(--dim)' : 'var(--text)', cursor: offset + LIMIT >= total ? 'default' : 'pointer', fontSize: '.78rem' }}>Next →</button>
                </div>
              </div>
            )}
          </>
        )}

        {/* Lockouts tab */}
        {tab === 'lockouts' && (
          <div style={{ display: 'flex', flexDirection: 'column', gap: 20 }}>
            <div style={{ display: 'flex', justifyContent: 'flex-end' }}>
              <button onClick={loadLockouts} style={{ padding: '7px 14px', background: 'var(--surface2)', border: '1px solid var(--border)', borderRadius: 6, color: 'var(--dim)', fontSize: '.82rem', cursor: 'pointer' }}>↻ Refresh</button>
            </div>

            {!lockouts && <div style={{ color: 'var(--dim)', fontSize: '.85rem' }}>Loading…</div>}

            {lockouts && lockouts.total === 0 && (
              <div style={{ textAlign: 'center', padding: 40, color: 'var(--dim)', fontSize: '.9rem' }}>
                ✓ No active lockouts
              </div>
            )}

            {lockouts?.email_lockouts?.length > 0 && (
              <div>
                <h4 style={{ margin: '0 0 10px', color: 'var(--dim)', fontSize: '.75rem', textTransform: 'uppercase', letterSpacing: '.06em' }}>Email Lockouts ({lockouts.email_lockouts.length})</h4>
                {lockouts.email_lockouts.map(({ email, attempts, locked_until, minutes_remaining }) => (
                  <div key={email} style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '12px 16px', background: 'var(--surface)', border: '1px solid var(--warn-dim)', borderRadius: 8, marginBottom: 6 }}>
                    <div>
                      <div style={{ fontWeight: 600, color: 'var(--text)', fontSize: '.84rem' }}>{email}</div>
                      <div style={{ color: 'var(--dim)', fontSize: '.74rem', marginTop: 3 }}>
                        {attempts} failed attempts · unlocks in {minutes_remaining} min ({new Date(locked_until).toLocaleString()})
                      </div>
                    </div>
                    <button
                      onClick={async () => {
                        // Find user by email and unlock
                        try {
                          const data = await apiGet(`/admin/users?search=${encodeURIComponent(email)}&limit=1`);
                          const u = data.users?.[0];
                          if (u) await apiPost(`/admin/users/${u.id}/unlock`);
                          await loadLockouts();
                        } catch (e) {}
                      }}
                      style={{ padding: '6px 14px', background: 'var(--warn-dim)', color: 'var(--warn)', border: '1px solid var(--warn-dim)', borderRadius: 6, fontSize: '.78rem', fontWeight: 700, cursor: 'pointer' }}>
                      🔓 Unlock
                    </button>
                  </div>
                ))}
              </div>
            )}

            {lockouts?.ip_lockouts?.length > 0 && (
              <div>
                <h4 style={{ margin: '0 0 10px', color: 'var(--dim)', fontSize: '.75rem', textTransform: 'uppercase', letterSpacing: '.06em' }}>IP Lockouts ({lockouts.ip_lockouts.length})</h4>
                {lockouts.ip_lockouts.map(({ ip, attempts, locked_until, minutes_remaining }) => (
                  <div key={ip} style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '12px 16px', background: 'var(--surface)', border: '1px solid var(--red-dim)', borderRadius: 8, marginBottom: 6 }}>
                    <div>
                      <div style={{ fontWeight: 600, color: 'var(--text)', fontFamily: 'monospace', fontSize: '.84rem' }}>{ip}</div>
                      <div style={{ color: 'var(--dim)', fontSize: '.74rem', marginTop: 3 }}>
                        {attempts} failed attempts · unlocks in {minutes_remaining} min
                      </div>
                    </div>
                    <button
                      onClick={async () => {
                        await apiPost('/admin/lockouts/clear-ip', { ip });
                        await loadLockouts();
                      }}
                      style={{ padding: '6px 14px', background: 'var(--red-dim)', color: 'var(--red)', border: '1px solid var(--red-dim)', borderRadius: 6, fontSize: '.78rem', fontWeight: 700, cursor: 'pointer' }}>
                      Clear IP
                    </button>
                  </div>
                ))}
              </div>
            )}
          </div>
        )}

        {/* User drawer */}
        {selectedUser && (
          <UserDrawer
            userId={selectedUser}
            onClose={() => setSelectedUser(null)}
            onAction={() => loadUsers(offset)}
          />
        )}
      </div>
    );
  }

  window.UserManagement = UserManagement;
  console.log('[WPSB] UserManagement loaded');
})();
