---
export const prerender = false;

import { isAuthenticated, ADMIN_COOKIE } from '@admin/lib/admin-auth';
import {
  buildTitleMetadataAvailabilitySql,
  buildTitleMetadataProjectionSql,
} from '@/lib/catalog-metadata';

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);
}

interface ScraperRun {
  id: number; chain_id: string; city_id: string;
  started_at: string; finished_at: string | null; status: string;
  movies_found: number; showtimes_found: number; error_message: string | null;
}
interface Stats { total: number; metadata_ready: number; visible: number; showtimes_total: number; showtimes_today: number; missing_posters: number; }
interface ComingSoonTitle { id: string; title_en: string; title_ar: string | null; release_date: string | null; metadata_popularity: number | null; metadata_rating: number | null; }
interface ShowtimeLeader { id: string; title_en: string; cinema_count: number; showtime_count: number; }
interface WaitlistCity { city_name: string; demand: number; }
interface BookingClickTitle { title_en: string; clicks: number; }
interface TitleAlertTitle  { title_en: string; signups: number; }

let stats: Stats = { total: 0, metadata_ready: 0, visible: 0, showtimes_total: 0, showtimes_today: 0, missing_posters: 0 };
let recentRuns: ScraperRun[] = [];
let chainHealth: ScraperRun[] = [];
let dbError: string | null = null;
let comingSoon: ComingSoonTitle[] = [];
let showtimeLeaders: ShowtimeLeader[] = [];
let waitlistHotspots: WaitlistCity[] = [];
let bookingClicks: BookingClickTitle[] = [];
let bookingClicksAvailable = false;
let titleAlerts: TitleAlertTitle[] = [];
let titleAlertsAvailable = false;

if (db) {
  try {
    const [total, metadataReady, visible, posterless, st, stToday] = await db.batch([
      db.prepare('SELECT COUNT(*) as n FROM titles'),
      db.prepare(`SELECT COUNT(*) as n FROM titles WHERE ${buildTitleMetadataAvailabilitySql()}`),
      db.prepare('SELECT COUNT(*) as n FROM titles WHERE poster_url IS NOT NULL AND synopsis_en IS NOT NULL AND duration_min IS NOT NULL'),
      db.prepare('SELECT COUNT(*) as n FROM titles WHERE poster_url IS NULL'),
      db.prepare('SELECT COUNT(*) as n FROM showtimes'),
      db.prepare("SELECT COUNT(*) as n FROM showtimes WHERE DATE(showtime) = DATE('now')"),
    ]);
    stats = {
      total: (total.results[0] as any)?.n ?? 0,
      metadata_ready: (metadataReady.results[0] as any)?.n ?? 0,
      visible: (visible.results[0] as any)?.n ?? 0,
      showtimes_total: (st.results[0] as any)?.n ?? 0,
      showtimes_today: (stToday.results[0] as any)?.n ?? 0,
      missing_posters: (posterless.results[0] as any)?.n ?? 0,
    };

    const [runs, health] = await db.batch([
      db.prepare('SELECT * FROM scraper_runs ORDER BY id DESC LIMIT 50'),
      db.prepare('SELECT * FROM scraper_runs WHERE id IN (SELECT MAX(id) FROM scraper_runs GROUP BY chain_id) ORDER BY chain_id'),
    ]);
    recentRuns = (runs.results as ScraperRun[]);
    chainHealth = (health.results as ScraperRun[]);
  } catch (e) {
    dbError = String(e);
  }

  // Release intelligence queries — separate try/catch so a failure here never breaks the dashboard
  try {
    const [csRes, slRes, wlRes] = await db.batch([
      db.prepare(`
        SELECT id, title_en, title_ar, release_date, ${buildTitleMetadataProjectionSql()}
        FROM titles WHERE status = 'coming_soon'
        ORDER BY COALESCE(release_date,'9999') ASC, COALESCE(metadata_popularity,0) DESC
        LIMIT 8
      `),
      db.prepare(`
        SELECT t.id, t.title_en,
               COUNT(DISTINCT s.cinema_id) AS cinema_count,
               COUNT(s.id) AS showtime_count
        FROM titles t
        JOIN showtimes s ON s.title_id = t.id
        WHERE t.status = 'now_playing'
        GROUP BY t.id
        ORDER BY cinema_count DESC, showtime_count DESC
        LIMIT 8
      `),
      db.prepare(`
        SELECT city_name, COUNT(*) AS demand
        FROM city_waitlist
        GROUP BY city_name
        ORDER BY demand DESC
        LIMIT 10
      `),
    ]);
    comingSoon = csRes.results as ComingSoonTitle[];
    showtimeLeaders = slRes.results as ShowtimeLeader[];
    waitlistHotspots = wlRes.results as WaitlistCity[];
  } catch (_) {
    // Intelligence queries failed — dashboard still works
  }

  // booking_clicks may not exist yet — handle gracefully
  try {
    const bcRes = await db.prepare(`
      SELECT t.title_en, COUNT(bc.id) AS clicks
      FROM booking_clicks bc
      JOIN titles t ON t.id = bc.title_id
      WHERE bc.clicked_at >= datetime('now', '-7 days')
      GROUP BY bc.title_id
      ORDER BY clicks DESC
      LIMIT 5
    `).all();
    bookingClicks = bcRes.results as BookingClickTitle[];
    bookingClicksAvailable = true;
  } catch (_) {
    // booking_clicks table not yet available
  }

  // title_alerts may not exist yet — handle gracefully
  try {
    const taRes = await db.prepare(`
      SELECT t.title_en, COUNT(ta.id) AS signups
      FROM title_alerts ta
      JOIN titles t ON t.id = ta.title_id
      GROUP BY ta.title_id
      ORDER BY signups DESC
      LIMIT 5
    `).all();
    titleAlerts = taRes.results as TitleAlertTitle[];
    titleAlertsAvailable = true;
  } catch (_) {
    // title_alerts table not yet available
  }
} else {
  dbError = 'D1 database binding not available.';
}

function duration(started: string, finished: string | null): string {
  if (!finished) return '—';
  const ms = new Date(finished).getTime() - new Date(started).getTime();
  if (ms < 0) return '—';
  const s = Math.round(ms / 1000);
  return s < 60 ? `${s}s` : `${Math.floor(s / 60)}m ${s % 60}s`;
}

function relativeTime(dt: string | null): string {
  if (!dt) return '—';
  const diffMs = Date.now() - new Date(dt).getTime();
  const m = Math.floor(diffMs / 60000);
  if (m < 1) return 'just now';
  if (m < 60) return `${m}m ago`;
  const h = Math.floor(m / 60);
  if (h < 24) return `${h}h ago`;
  return `${Math.floor(h / 24)}d ago`;
}

---
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <title>CultRoll Admin</title>
  <link rel="icon" href="/favicon.svg" type="image/svg+xml" />
  <style>
    *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
    body { background: #0a0a0a; color: #e6e6e6; font-family: system-ui, -apple-system, sans-serif; font-size: 0.9rem; }
    a { color: #b78a3c; text-decoration: none; }
    a:hover { text-decoration: underline; }

    /* Layout */
    .sidebar { position: fixed; top: 0; left: 0; width: 220px; height: 100vh; background: #111; border-right: 1px solid #222; padding: 1.5rem 1rem; display: flex; flex-direction: column; gap: 0.25rem; overflow-y: auto; }
    .main { margin-left: 220px; padding: 2rem; max-width: 1200px; }
    .logo { display: flex; align-items: center; gap: 0.5rem; font-weight: 700; letter-spacing: 0.1em; color: #fff; font-size: 0.95rem; margin-bottom: 1.5rem; }
    .nav-section { font-size: 0.7rem; font-weight: 600; color: #555; text-transform: uppercase; letter-spacing: 0.1em; padding: 0.75rem 0.5rem 0.25rem; }
    .nav-link { display: block; padding: 0.5rem 0.75rem; border-radius: 6px; color: #aaa; font-size: 0.85rem; }
    .nav-link:hover, .nav-link.active { background: #1a1a1a; color: #fff; text-decoration: none; }
    .logout { margin-top: auto; padding-top: 1rem; border-top: 1px solid #222; }

    /* Page header */
    .page-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 1.5rem; }
    .page-header h1 { font-size: 1.3rem; font-weight: 700; color: #fff; }
    .page-header .timestamp { font-size: 0.75rem; color: #555; }

    /* Stats grid */
    .stats-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 1rem; margin-bottom: 2rem; }
    .stat-card { background: #161616; border: 1px solid #222; border-radius: 10px; padding: 1.1rem; }
    .stat-card .label { font-size: 0.72rem; font-weight: 600; color: #666; text-transform: uppercase; letter-spacing: 0.08em; margin-bottom: 0.4rem; }
    .stat-card .value { font-size: 1.6rem; font-weight: 700; color: #fff; line-height: 1; }
    .stat-card .sub { font-size: 0.72rem; color: #555; margin-top: 0.3rem; }
    .stat-card.warn .value { color: #f59e0b; }
    .stat-card.ok .value { color: #34d399; }

    /* Section */
    .section { margin-bottom: 2rem; }
    .section-title { font-size: 0.8rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.1em; color: #666; margin-bottom: 0.75rem; }

    /* Chain health */
    .chain-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 0.75rem; margin-bottom: 2rem; }
    .chain-card { background: #161616; border: 1px solid #222; border-radius: 8px; padding: 1rem; display: flex; flex-direction: column; gap: 0.4rem; }
    .chain-card .chain-name { font-weight: 600; color: #fff; font-size: 0.9rem; display: flex; align-items: center; gap: 0.5rem; }
    .chain-card .chain-meta { display: flex; gap: 1rem; font-size: 0.75rem; color: #666; }
    .chain-card .chain-counts { font-size: 0.8rem; color: #aaa; }

    /* Badges */
    .badge { display: inline-block; padding: 0.2em 0.55em; border-radius: 4px; font-size: 0.7rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; }
    .badge-success { background: #0d2e1e; color: #34d399; }
    .badge-failed { background: #2a1010; color: #f87171; }
    .badge-running { background: #1a200a; color: #facc15; }
    .badge-partial { background: #1a160a; color: #fb923c; }

    /* Table */
    .table-wrap { overflow-x: auto; }
    table { width: 100%; border-collapse: collapse; font-size: 0.82rem; }
    thead th { background: #111; color: #666; font-size: 0.7rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; padding: 0.5rem 0.75rem; text-align: left; border-bottom: 1px solid #222; }
    tbody tr { border-bottom: 1px solid #1a1a1a; }
    tbody tr:hover { background: #141414; }
    tbody td { padding: 0.55rem 0.75rem; color: #ccc; vertical-align: middle; }
    .mono { font-family: 'Courier New', monospace; font-size: 0.78rem; }
    .error-cell { color: #f87171; font-size: 0.75rem; max-width: 200px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

    .trigger-btn { background: #1a1a1a; border: 1px solid #333; border-radius: 6px; padding: 0.5rem 0.9rem; color: #e6e6e6; font-size: 0.8rem; cursor: pointer; display: inline-flex; align-items: center; gap: 0.4rem; }

    /* DB error */
    .db-error { background: #2a1010; border: 1px solid #5a2020; border-radius: 8px; padding: 1rem; color: #f87171; margin-bottom: 1.5rem; font-size: 0.85rem; }

    /* Release intelligence */
    .intel-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 1.25rem; margin-bottom: 2rem; }
    @media (max-width: 900px) { .intel-grid { grid-template-columns: 1fr; } }
    .intel-card { background: #161616; border: 1px solid #222; border-radius: 10px; overflow: hidden; }
    .intel-card-header { padding: 0.75rem 1rem; border-bottom: 1px solid #222; display: flex; align-items: baseline; gap: 0.6rem; }
    .intel-card-header .card-title { font-size: 0.75rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.09em; color: #888; }
    .intel-card-header .card-hint { font-size: 0.7rem; color: #444; }
    .intel-card table { width: 100%; font-size: 0.8rem; }
    .intel-card thead th { background: transparent; color: #555; padding: 0.45rem 0.9rem; font-size: 0.68rem; }
    .intel-card tbody td { padding: 0.45rem 0.9rem; border-bottom: 1px solid #1a1a1a; color: #ccc; }
    .intel-card tbody tr:last-child td { border-bottom: none; }
    .intel-card tbody tr:hover { background: #111; }
    .intel-card .empty { padding: 1.2rem 0.9rem; color: #444; font-size: 0.8rem; }
    .tag-gold { color: #b78a3c; font-weight: 600; }
    .tag-dim { color: #555; font-size: 0.72rem; }
    .pill { display: inline-block; padding: 0.15em 0.5em; border-radius: 99px; font-size: 0.68rem; font-weight: 700; background: #1c1c1c; color: #666; }
    .pill-green { background: #0d2e1e; color: #34d399; }
    .release-intel-header { display: flex; align-items: center; gap: 0.75rem; margin-bottom: 0.75rem; flex-wrap: wrap; }
    .release-intel-header h2 { font-size: 1rem; font-weight: 700; color: #fff; }
    .release-intel-header .sub { font-size: 0.75rem; color: #444; }
    .export-links { margin-left: auto; display: flex; gap: 0.5rem; }
    .export-btn { display: inline-flex; align-items: center; gap: 0.3em; padding: 0.3em 0.75em; border-radius: 5px; background: #1a1a1a; border: 1px solid #2e2e2e; color: #b78a3c; font-size: 0.72rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.07em; text-decoration: none; transition: background 0.15s, border-color 0.15s; }
    .export-btn:hover { background: #222; border-color: #b78a3c; text-decoration: none; }
    .intel-bottom { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 1.25rem; margin-bottom: 2rem; }
    @media (max-width: 1100px) { .intel-bottom { grid-template-columns: 1fr 1fr; } }
    @media (max-width: 700px)  { .intel-bottom { grid-template-columns: 1fr; } }
  </style>
</head>
<body>
  <!-- Sidebar -->
  <nav class="sidebar">
    <div class="logo">
      <svg viewBox="0 0 32 32" width="22" height="22" xmlns="http://www.w3.org/2000/svg">
        <rect width="32" height="32" rx="6" fill="#0a0a0a"/>
        <circle cx="16" cy="16" r="10" fill="none" stroke="#e6e6e6" stroke-width="1.5"/>
        <circle cx="16" cy="16" r="3.5" fill="#e6e6e6"/>
        <circle cx="16" cy="7" r="1.5" fill="#0a0a0a"/>
        <circle cx="16" cy="25" r="1.5" fill="#0a0a0a"/>
        <circle cx="7" cy="16" r="1.5" fill="#0a0a0a"/>
        <circle cx="25" cy="16" r="1.5" fill="#0a0a0a"/>
        <circle cx="9.7" cy="9.7" r="1.5" fill="#0a0a0a"/>
        <circle cx="22.3" cy="22.3" r="1.5" fill="#0a0a0a"/>
        <circle cx="22.3" cy="9.7" r="1.5" fill="#0a0a0a"/>
        <circle cx="9.7" cy="22.3" r="1.5" fill="#0a0a0a"/>
      </svg>
      CULTROLL
    </div>
    <div class="nav-section">Overview</div>
    <a href="/admin" class="nav-link active">Dashboard</a>
    <div class="nav-section">Scraper</div>
    <a href="/admin/scrapers" class="nav-link">Sources</a>
    <a href="/admin/cinemas" class="nav-link">Cinemas</a>
    <a href="/admin/pipelines" class="nav-link">Pipeline Jobs</a>
    <a href="/admin/runs" class="nav-link">All Runs</a>
    <div class="nav-section">Account</div>
    <a href="/admin/users" class="nav-link">Users</a>
    <a href="/admin/password" class="nav-link">Change Password</a>
    <div class="nav-section">Data</div>
    <a href="/admin/titles" class="nav-link">Titles</a>
    <a href="/admin/cast" class="nav-link">Cast</a>
    <a href="/admin/genres" class="nav-link">Genres</a>
    <a href="/" class="nav-link" target="_blank">↗ Live Site</a>
    <div class="logout">
      <form method="POST" action="/api/admin/logout">
        <button type="submit" class="trigger-btn" style="width:100%;justify-content:center">Sign out</button>
      </form>
    </div>
  </nav>

  <!-- Main -->
  <main class="main">
    <div class="page-header">
      <h1>Dashboard</h1>
      <span class="timestamp">Generated at {new Date().toISOString().replace('T',' ').slice(0,19)} UTC</span>
    </div>

    {dbError && <div class="db-error">⚠ DB error: {dbError}</div>}

    <!-- Stats cards -->
    <div class="stats-grid">
      <div class={`stat-card ${stats.visible < stats.total ? 'warn' : 'ok'}`}>
        <div class="label">Visible Movies</div>
        <div class="value">{stats.visible}</div>
        <div class="sub">of {stats.total} total</div>
      </div>
      <div class={`stat-card ${stats.metadata_ready === stats.total && stats.total > 0 ? 'ok' : 'warn'}`}>
        <div class="label">Metadata Ready</div>
        <div class="value">{stats.metadata_ready}</div>
        <div class="sub">{stats.total - stats.metadata_ready} pending</div>
      </div>
      <div class={`stat-card ${stats.missing_posters > 0 ? 'warn' : 'ok'}`}>
        <div class="label">Missing Posters</div>
        <div class="value">{stats.missing_posters}</div>
        <div class="sub">of {stats.total} movies</div>
      </div>
      <div class="stat-card">
        <div class="label">Total Showtimes</div>
        <div class="value">{stats.showtimes_total}</div>
        <div class="sub">{stats.showtimes_today} today</div>
      </div>
    </div>

    <!-- Release Intelligence -->
    <div style="margin-bottom:0.75rem">
      <div class="release-intel-header">
        <h2>Release Intelligence</h2>
        <span class="sub">Week snapshot for exhibitor &amp; distributor conversations</span>
        <div class="export-links">
          <a href="/api/admin/reports/release-pulse?format=csv" class="export-btn">↓ CSV</a>
          <a href="/api/admin/reports/release-pulse?format=json" class="export-btn">↓ JSON</a>
        </div>
      </div>
    </div>

    <div class="intel-grid">
      <!-- Coming Soon -->
      <div class="intel-card">
        <div class="intel-card-header">
          <span class="card-title">Coming Soon</span>
          <span class="card-hint">Upcoming titles to pitch, sorted by release date</span>
        </div>
        {comingSoon.length === 0
          ? <div class="empty">No coming-soon titles in database.</div>
          : (
            <table>
              <thead>
                <tr>
                  <th>Title</th>
                  <th>Release</th>
                  <th>Popularity</th>
                  <th>Rating</th>
                </tr>
              </thead>
              <tbody>
                {comingSoon.map(t => (
                  <tr>
                    <td>
                      <a href={`/admin/titles/${t.id}`} style="color:#e6e6e6;font-weight:500">{t.title_en}</a>
                      {t.title_ar && <div class="tag-dim" dir="rtl">{t.title_ar}</div>}
                    </td>
                    <td class="mono">{t.release_date ?? '—'}</td>
                    <td class="tag-gold">{t.metadata_popularity != null ? t.metadata_popularity.toFixed(1) : '—'}</td>
                    <td>{t.metadata_rating != null ? t.metadata_rating.toFixed(1) : '—'}</td>
                  </tr>
                ))}
              </tbody>
            </table>
          )
        }
      </div>

      <!-- Showtime Leaders -->
      <div class="intel-card">
        <div class="intel-card-header">
          <span class="card-title">Showtime Leaders</span>
          <span class="card-hint">Now-playing titles by cinema breadth</span>
        </div>
        {showtimeLeaders.length === 0
          ? <div class="empty">No showtime data available.</div>
          : (
            <table>
              <thead>
                <tr>
                  <th>Title</th>
                  <th>Cinemas</th>
                  <th>Showtimes</th>
                </tr>
              </thead>
              <tbody>
                {showtimeLeaders.map((t, i) => (
                  <tr>
                    <td>
                      <span class={i < 3 ? 'tag-gold' : ''}>{i + 1}.</span>{' '}
                      <a href={`/admin/titles/${t.id}`} style="color:#e6e6e6">{t.title_en}</a>
                    </td>
                    <td><span class="pill pill-green">{t.cinema_count}</span></td>
                    <td class="mono">{t.showtime_count}</td>
                  </tr>
                ))}
              </tbody>
            </table>
          )
        }
      </div>
    </div>

    <!-- Demand signals row -->
    <div class="intel-bottom">
      <!-- City Waitlist Hotspots -->
      <div class="intel-card">
        <div class="intel-card-header">
          <span class="card-title">City Demand</span>
          <span class="card-hint">Waitlist signups by city</span>
        </div>
        {waitlistHotspots.length === 0
          ? <div class="empty">No waitlist signups yet.</div>
          : (
            <table>
              <thead>
                <tr><th>City</th><th>Signups</th></tr>
              </thead>
              <tbody>
                {waitlistHotspots.map(w => (
                  <tr>
                    <td>{w.city_name}</td>
                    <td class="tag-gold" style="font-weight:600">{w.demand}</td>
                  </tr>
                ))}
              </tbody>
            </table>
          )
        }
      </div>

      <!-- Booking Clicks (conditional) -->
      <div class="intel-card">
        <div class="intel-card-header">
          <span class="card-title">Booking Intent</span>
          <span class="card-hint">
            {bookingClicksAvailable ? 'Clicks on booking links (7 days)' : 'Table not yet available'}
          </span>
        </div>
        {!bookingClicksAvailable
          ? <div class="empty" style="color:#333">Waiting for <code style="color:#555;font-size:0.75rem">booking_clicks</code> table.</div>
          : bookingClicks.length === 0
            ? <div class="empty">No booking clicks recorded in the last 7 days.</div>
            : (
              <table>
                <thead>
                  <tr><th>Title</th><th>Clicks (7d)</th></tr>
                </thead>
                <tbody>
                  {bookingClicks.map(bc => (
                    <tr>
                      <td>{bc.title_en}</td>
                      <td class="tag-gold" style="font-weight:600">{bc.clicks}</td>
                    </tr>
                  ))}
                </tbody>
              </table>
            )
        }
      </div>

      <!-- Title Alert Demand (conditional) -->
      <div class="intel-card">
        <div class="intel-card-header">
          <span class="card-title">Title Alert Demand</span>
          <span class="card-hint">
            {titleAlertsAvailable ? 'Signups per upcoming title' : 'Table not yet available'}
          </span>
        </div>
        {!titleAlertsAvailable
          ? <div class="empty" style="color:#333">Waiting for <code style="color:#555;font-size:0.75rem">title_alerts</code> table.</div>
          : titleAlerts.length === 0
            ? <div class="empty">No title alert signups recorded yet.</div>
            : (
              <table>
                <thead>
                  <tr><th>Title</th><th>Signups</th></tr>
                </thead>
                <tbody>
                  {titleAlerts.map(ta => (
                    <tr>
                      <td>{ta.title_en}</td>
                      <td class="tag-gold" style="font-weight:600">{ta.signups}</td>
                    </tr>
                  ))}
                </tbody>
              </table>
            )
        }
      </div>
    </div>

    <div class="section">
      <div class="section-title">Scraper Worker Queue</div>
      <div class="stat-card">
        <div class="label">Operational Status</div>
        <div class="value" id="scraper-worker-state" style="font-size:1.2rem">Loading...</div>
        <div class="sub" id="scraper-worker-meta">Fetching secured /status and /stats</div>
        <div class="sub" id="scraper-worker-detail" style="margin-top:.4rem"></div>
      </div>
    </div>

    <!-- Chain health -->
    <div class="section-title">Source Adapter Health — Last Run</div>
    <div class="chain-grid">
      {chainHealth.length === 0 && <p style="color:#555;font-size:0.85rem">No runs recorded yet.</p>}
      {chainHealth.map(run => (
        <div class="chain-card">
          <div class="chain-name">
            <span class={`badge badge-${run.status}`}>{run.status}</span>
            {run.chain_id}
          </div>
          <div class="chain-counts">
            {run.movies_found} movies · {run.showtimes_found} showtimes
          </div>
          <div class="chain-meta">
            <span>{relativeTime(run.started_at)}</span>
            <span>took {duration(run.started_at, run.finished_at)}</span>
            <a href={`/admin/runs/${run.id}`} style="margin-left:auto">details →</a>
          </div>
          {run.error_message && <div style="font-size:0.72rem;color:#f87171;margin-top:0.2rem" title={run.error_message}>{run.error_message.slice(0, 80)}</div>}
        </div>
      ))}
    </div>

    <!-- Recent runs table -->
    <div class="section-title" style="margin-top:2rem">Recent Runs (last 50)</div>
    <div class="table-wrap">
      <table>
        <thead>
          <tr>
            <th>#</th>
            <th>Chain</th>
            <th>City</th>
            <th>Status</th>
            <th>Started</th>
            <th>Duration</th>
            <th>Movies</th>
            <th>Showtimes</th>
            <th>Error</th>
          </tr>
        </thead>
        <tbody>
          {recentRuns.length === 0 && (
            <tr><td colspan="9" style="color:#555;text-align:center;padding:1.5rem">No runs recorded.</td></tr>
          )}
          {recentRuns.map(run => (
            <tr>
              <td><a href={`/admin/runs/${run.id}`} class="mono">#{run.id}</a></td>
              <td class="mono">{run.chain_id}</td>
              <td class="mono">{run.city_id ?? '—'}</td>
              <td><span class={`badge badge-${run.status}`}>{run.status}</span></td>
              <td title={run.started_at}>{relativeTime(run.started_at)}</td>
              <td>{duration(run.started_at, run.finished_at)}</td>
              <td>{run.movies_found ?? 0}</td>
              <td>{run.showtimes_found ?? 0}</td>
              <td class="error-cell" title={run.error_message ?? ''}>{run.error_message ?? ''}</td>
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  </main>

  <script is:inline>
    (async () => {
      const stateEl = document.getElementById('scraper-worker-state');
      const metaEl = document.getElementById('scraper-worker-meta');
      const detailEl = document.getElementById('scraper-worker-detail');
      try {
        const res = await fetch('/api/admin/scraper-status');
        const payload = await res.json();
        if (!res.ok) throw new Error(payload?.error ?? 'failed to fetch status');

        const status = payload?.status ?? {};
        const stats = payload?.stats ?? {};
        const sources = Array.isArray(status.sources) ? status.sources : [];
        const pipelineJobs = Array.isArray(status.pipeline_jobs) ? status.pipeline_jobs : [];
        const chain = pipelineJobs.map((job) => job?.adapter).filter(Boolean).join(' -> ');

        stateEl.textContent = stats.queue_enabled ? 'Queue enabled' : 'Queue disabled';
        stateEl.style.color = stats.queue_enabled ? '#34d399' : '#f87171';
        metaEl.textContent =
          `${sources.length} sources, ${stats.sources_due_now ?? 0} due now, ${stats.runs_last_24h ?? 0} runs in last 24h`;
        detailEl.textContent =
          `Failed 24h: ${stats.failed_runs_last_24h ?? 0} • Latest run: ${stats.latest_last_run_at ?? '—'} • Pipeline: ${chain || 'n/a'}`;
      } catch (err) {
        stateEl.textContent = 'Unavailable';
        stateEl.style.color = '#f87171';
        metaEl.textContent = err instanceof Error ? err.message : String(err);
        detailEl.textContent = '';
      }
    })();
  </script>

</body>
</html>
