---
export const prerender = false;
import { isAuthenticated, ADMIN_COOKIE } from '@admin/lib/admin-auth';
interface Env { DB: D1Database }
const runtime = Astro.locals.runtime as { env: Env } | undefined;
const db = runtime?.env?.DB;
if (!db || !await isAuthenticated(Astro.cookies.get(ADMIN_COOKIE)?.value, db)) {
  return Astro.redirect('/admin/login', 302);
}
---
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Users — CultRoll Admin</title>
  <style>
    :root {
      --bg: #0f0f0f; --surface: #141414; --surface2: #1a1a1a;
      --border: #222; --border2: #2e2e2e;
      --text: #e0e0e0; --text-dim: #888; --text-muted: #555;
      --gold: #b07d2e; --gold-h: #c8922f; --green: #4caf80; --red: #ef5350;
    }
    * { box-sizing: border-box; margin: 0; padding: 0; }
    body { font-family: system-ui, -apple-system, sans-serif; background: var(--bg); color: var(--text); min-height: 100vh; font-size: 13px; }
    .topbar { background: var(--surface); border-bottom: 1px solid var(--border); padding: 0 1.25rem; height: 46px; display: flex; align-items: center; gap: 0.6rem; position: sticky; top: 0; z-index: 20; }
    .topbar a { color: var(--text-dim); text-decoration: none; font-size: 0.8rem; }
    .topbar a:hover { color: var(--text); }
    .topbar .sep { color: var(--border2); }
    .topbar h1 { font-size: 0.9rem; font-weight: 600; }
    .topbar-count { margin-left: auto; font-size: 0.75rem; color: var(--text-muted); }
    .cms { padding: 0.75rem 1.25rem 2rem; }
    .toolbar { display: flex; align-items: center; gap: 0.5rem; margin-bottom: 0.75rem; flex-wrap: wrap; }
    .search-wrap { position: relative; }
    .search-wrap .ico { position: absolute; left: 0.55rem; top: 50%; transform: translateY(-50%); color: var(--text-muted); pointer-events: none; }
    .search-input { width: 260px; padding: 0.35rem 0.65rem 0.35rem 1.85rem; background: var(--surface2); border: 1px solid var(--border2); border-radius: 5px; color: var(--text); font-size: 0.8rem; }
    .search-input:focus, .ctrl-sel:focus { outline: none; border-color: var(--gold); }
    .filter-chip { padding: 0.25rem 0.6rem; background: var(--surface2); border: 1px solid var(--border2); border-radius: 100px; color: var(--text-dim); font-size: 0.72rem; cursor: pointer; white-space: nowrap; transition: all 0.12s; }
    .filter-chip.active { background: var(--gold); border-color: var(--gold); color: #fff; font-weight: 600; }
    .spacer { flex: 1; }
    .ctrl-sel { padding: 0.25rem 0.5rem; background: var(--surface2); border: 1px solid var(--border2); border-radius: 5px; color: var(--text-dim); font-size: 0.75rem; cursor: pointer; }
    .tbl-wrap { overflow-x: auto; border: 1px solid var(--border); border-radius: 6px; }
    table { width: 100%; border-collapse: collapse; }
    thead th { padding: 0.5rem 0.65rem; text-align: left; font-size: 0.72rem; font-weight: 600; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.05em; background: var(--surface); border-bottom: 1px solid var(--border); white-space: nowrap; }
    tbody tr { border-bottom: 1px solid var(--border); transition: background 0.1s; }
    tbody tr:hover { background: var(--surface2); }
    td { padding: 0.55rem 0.65rem; vertical-align: middle; }
    .state-row td { text-align: center; padding: 3rem 1rem; color: var(--text-muted); }
    .status { display: inline-flex; align-items: center; padding: 2px 8px; border-radius: 999px; font-size: 0.68rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; }
    .status-active { background: rgba(76, 175, 128, 0.15); color: var(--green); border: 1px solid rgba(76, 175, 128, 0.3); }
    .status-suspended { background: rgba(239, 83, 80, 0.12); color: var(--red); border: 1px solid rgba(239, 83, 80, 0.25); }
    .provider { display: inline-block; padding: 2px 6px; border-radius: 4px; background: #1d1d1d; color: var(--text-dim); font-size: 0.7rem; margin-right: 4px; }
    .btn-toggle { padding: 0.25rem 0.55rem; background: transparent; border: 1px solid var(--border2); color: var(--text-dim); border-radius: 4px; font-size: 0.72rem; cursor: pointer; }
    .btn-toggle:hover { border-color: var(--gold); color: var(--gold); }
    .pag { display: flex; align-items: center; justify-content: space-between; margin-top: 0.85rem; flex-wrap: wrap; gap: 0.5rem; }
    .pag-info { font-size: 0.72rem; color: var(--text-muted); }
    .pag-btns { display: flex; gap: 3px; flex-wrap: wrap; align-items: center; }
    .pg { min-width: 28px; height: 28px; padding: 0 4px; display: inline-flex; align-items: center; justify-content: center; background: var(--surface2); border: 1px solid var(--border2); color: var(--text-dim); border-radius: 4px; font-size: 0.75rem; cursor: pointer; }
    .pg.active { background: var(--gold); border-color: var(--gold); color: #fff; font-weight: 700; cursor: default; }
    .toast { position: fixed; bottom: 1.25rem; right: 1.25rem; padding: 0.55rem 0.9rem; border-radius: 5px; font-size: 0.8rem; font-weight: 500; opacity: 0; transform: translateY(6px); transition: all 0.2s; pointer-events: none; z-index: 200; }
    .toast.show { opacity: 1; transform: none; }
    .toast-ok { background: rgba(76, 175, 128, 0.15); color: var(--green); border: 1px solid rgba(76, 175, 128, 0.3); }
    .toast-err { background: rgba(239, 83, 80, 0.12); color: var(--red); border: 1px solid rgba(239, 83, 80, 0.25); }
  </style>
</head>
<body>
<div class="topbar">
  <a href="/admin">← Dashboard</a>
  <span class="sep">/</span>
  <h1>Users</h1>
  <a href="/admin/titles" style="margin-left:0.35rem">Titles</a>
  <span class="topbar-count" id="topbar-count"></span>
</div>

<div class="cms">
  <div class="toolbar">
    <div class="search-wrap">
      <svg class="ico" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.35-4.35"/></svg>
      <input id="search" class="search-input" type="search" placeholder="Search email or name…" autocomplete="off" />
    </div>
    <button class="filter-chip active" data-filter="">All</button>
    <button class="filter-chip" data-filter="active">Active</button>
    <button class="filter-chip" data-filter="suspended">Suspended</button>
    <div class="spacer"></div>
    <select id="page-size" class="ctrl-sel">
      <option value="50" selected>50 / page</option>
      <option value="100">100 / page</option>
      <option value="200">200 / page</option>
    </select>
  </div>

  <div class="tbl-wrap">
    <table>
      <thead>
        <tr>
          <th>Email</th>
          <th>Name</th>
          <th>Providers</th>
          <th>Title Alerts</th>
          <th>City Waitlist</th>
          <th>Sessions</th>
          <th>Last Login</th>
          <th>Status</th>
          <th></th>
        </tr>
      </thead>
      <tbody id="tbody">
        <tr class="state-row"><td colspan="9">Loading…</td></tr>
      </tbody>
    </table>
  </div>

  <div class="pag" id="pag" style="display:none">
    <span class="pag-info" id="pag-info"></span>
    <div class="pag-btns" id="pag-btns"></div>
  </div>
</div>

<div class="toast" id="toast"></div>

<script>
  const sp = new URLSearchParams(location.search);
  let q = sp.get('q') || '';
  let statusFilter = sp.get('status') || '';
  let page = parseInt(sp.get('page') || '1', 10) || 1;
  let pageSize = parseInt(sp.get('size') || '50', 10) || 50;
  let total = 0;
  let searchTimer;

  const tbody = document.getElementById('tbody');
  const pag = document.getElementById('pag');
  const pagInfo = document.getElementById('pag-info');
  const pagBtns = document.getElementById('pag-btns');
  const searchEl = document.getElementById('search');
  const pageSizeEl = document.getElementById('page-size');
  const topbarCount = document.getElementById('topbar-count');
  const toastEl = document.getElementById('toast');

  searchEl.value = q;
  pageSizeEl.value = String(pageSize);
  setActiveFilter(statusFilter);

  function esc(value) {
    return String(value ?? '').replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
  }
  function pushURL() {
    const params = new URLSearchParams();
    if (q) params.set('q', q);
    if (statusFilter) params.set('status', statusFilter);
    if (page > 1) params.set('page', String(page));
    if (pageSize !== 50) params.set('size', String(pageSize));
    const qs = params.toString();
    history.replaceState({}, '', qs ? `?${qs}` : location.pathname);
  }
  function setActiveFilter(value) {
    document.querySelectorAll('[data-filter]').forEach((el) => el.classList.toggle('active', el.dataset.filter === value));
  }
  function showToast(message, kind = 'ok') {
    toastEl.textContent = message;
    toastEl.className = `toast toast-${kind} show`;
    setTimeout(() => toastEl.className = `toast toast-${kind}`, 2200);
  }
  function formatDate(value) {
    if (!value) return '—';
    try { return new Date(value).toLocaleString(); } catch { return value; }
  }
  function providerPills(user) {
    const pills = [];
    if (user.google_sub) pills.push('<span class="provider">Google</span>');
    if (user.email_verified_at) pills.push('<span class="provider">Email link</span>');
    return pills.join('') || '<span class="provider">Pending</span>';
  }
  function renderRows(users) {
    if (!users.length) {
      tbody.innerHTML = '<tr class="state-row"><td colspan="9">No users found.</td></tr>';
      return;
    }
    tbody.innerHTML = users.map((user) => `
      <tr>
        <td>${esc(user.email)}</td>
        <td>${esc(user.display_name || '—')}</td>
        <td>${providerPills(user)}</td>
        <td>${esc(user.title_alert_count)}</td>
        <td>${esc(user.city_waitlist_count)}</td>
        <td>${esc(user.active_session_count)}</td>
        <td>${esc(formatDate(user.last_login_at))}</td>
        <td><span class="status status-${esc(user.status)}">${esc(user.status)}</span></td>
        <td>
          <button class="btn-toggle" data-user-id="${esc(user.id)}" data-next-status="${user.status === 'active' ? 'suspended' : 'active'}">
            ${user.status === 'active' ? 'Suspend' : 'Reactivate'}
          </button>
        </td>
      </tr>
    `).join('');
  }
  function renderPagination() {
    if (total <= pageSize) {
      pag.style.display = 'none';
      return;
    }
    pag.style.display = 'flex';
    const totalPages = Math.ceil(total / pageSize);
    pagInfo.textContent = `Showing ${(page - 1) * pageSize + 1}-${Math.min(page * pageSize, total)} of ${total}`;
    const buttons = [];
    for (let p = 1; p <= totalPages; p += 1) {
      if (p === 1 || p === totalPages || Math.abs(p - page) <= 1) {
        buttons.push(`<button class="pg ${p === page ? 'active' : ''}" data-page="${p}" ${p === page ? 'disabled' : ''}>${p}</button>`);
      } else if (buttons[buttons.length - 1] !== '<span class="pg-ellipsis">…</span>') {
        buttons.push('<span class="pg-ellipsis">…</span>');
      }
    }
    pagBtns.innerHTML = buttons.join('');
  }
  async function load() {
    pushURL();
    tbody.innerHTML = '<tr class="state-row"><td colspan="9">Loading…</td></tr>';
    const params = new URLSearchParams({ limit: String(pageSize), offset: String((page - 1) * pageSize) });
    if (q) params.set('q', q);
    if (statusFilter) params.set('status', statusFilter);
    const response = await fetch(`/api/admin/users?${params.toString()}`);
    const payload = await response.json();
    total = payload.total || 0;
    topbarCount.textContent = `${total} users`;
    renderRows(payload.users || []);
    renderPagination();
  }
  document.addEventListener('click', async (event) => {
    const target = event.target;
    if (target.matches('[data-filter]')) {
      statusFilter = target.dataset.filter || '';
      page = 1;
      setActiveFilter(statusFilter);
      load();
      return;
    }
    if (target.matches('[data-page]')) {
      page = parseInt(target.dataset.page || '1', 10) || 1;
      load();
      return;
    }
    if (target.matches('.btn-toggle')) {
      const userId = target.dataset.userId;
      const nextStatus = target.dataset.nextStatus;
      target.disabled = true;
      try {
        const response = await fetch(`/api/admin/users/${userId}`, {
          method: 'PATCH',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ status: nextStatus }),
        });
        const payload = await response.json().catch(() => ({}));
        if (!response.ok) throw new Error(payload.error || 'Could not update user.');
        showToast(nextStatus === 'suspended' ? 'User suspended' : 'User reactivated', 'ok');
        load();
      } catch (error) {
        showToast(error instanceof Error ? error.message : 'Could not update user.', 'err');
      } finally {
        target.disabled = false;
      }
    }
  });
  searchEl.addEventListener('input', () => {
    clearTimeout(searchTimer);
    searchTimer = setTimeout(() => {
      q = searchEl.value.trim();
      page = 1;
      load();
    }, 180);
  });
  pageSizeEl.addEventListener('change', () => {
    pageSize = parseInt(pageSizeEl.value, 10) || 50;
    page = 1;
    load();
  });
  load();
</script>
</body>
</html>
