/* Admin views - Overview, Matches, Create, Settle, Draw. */

/* ============================================================
   OVERVIEW
   ============================================================ */
function StatCard({ label, value, accent = 'orange', delta }) {
  const colors = {
    orange: 'var(--orange)',
    ink:    'var(--ink)',
    green:  'var(--success)',
    grey:   'var(--grey-400)',
  };
  return (
    <div style={{
      background: '#fff', border: '1.5px solid var(--grey-200)',
      padding: '18px 20px', position: 'relative', overflow: 'hidden',
    }}>
      <div style={{
        position: 'absolute', top: 0, left: 0, bottom: 0, width: 4, background: colors[accent],
      }} />
      <div style={{ fontSize: 11, letterSpacing: '0.16em', textTransform: 'uppercase', color: 'var(--grey-500)', fontWeight: 700 }}>
        {label}
      </div>
      <div className="font-display" style={{ fontSize: 32, lineHeight: 1, marginTop: 8, letterSpacing: '-0.02em', color: accent === 'green' ? colors.green : 'var(--ink)' }}>
        {value}
      </div>
      {delta && (
        <div style={{ marginTop: 6, fontSize: 12, color: 'var(--success)', fontWeight: 600 }}>
          {delta}
        </div>
      )}
    </div>
  );
}

/* Campaign-health signal palette */
const HEALTH_COLORS = { good: 'var(--success)', warn: '#d97706', bad: '#dc2626' };
const HEALTH_LABELS = { good: 'On track', warn: 'Needs attention', bad: 'Off track' };

function SignalRow({ label, value, status, note, action }) {
  return (
    <div style={{ display: 'flex', alignItems: 'flex-start', gap: 12, padding: '12px 0', borderBottom: '1px solid var(--grey-100)' }}>
      <span style={{ width: 10, height: 10, borderRadius: '50%', background: HEALTH_COLORS[status], marginTop: 4, flexShrink: 0 }} />
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{ display: 'flex', justifyContent: 'space-between', gap: 10, flexWrap: 'wrap' }}>
          <span style={{ fontWeight: 700, fontSize: 13 }}>{label}</span>
          <span className="font-mono" style={{ fontSize: 12.5, fontWeight: 700, color: HEALTH_COLORS[status] }}>{value}</span>
        </div>
        <div style={{ fontSize: 12.5, color: 'var(--grey-600)', marginTop: 2, lineHeight: 1.45 }}>{note}</div>
        {action && status !== 'good' && (
          <div style={{ fontSize: 11.5, color: HEALTH_COLORS[status], fontWeight: 700, marginTop: 5, display: 'flex', gap: 5, alignItems: 'flex-start' }}>
            <span style={{ flexShrink: 0 }}>→</span><span>{action}</span>
          </div>
        )}
      </div>
    </div>
  );
}

function AdminOverview({ ctx }) {
  const preds = ctx.livePredictions;
  const open = ctx.matchesState.filter(m => m.status === 'OPEN');
  const closed = ctx.matchesState.filter(m => m.status === 'CLOSED');
  const settled = ctx.matchesState.filter(m => m.status === 'SETTLED');

  // Bill data (bills issued, votes earned/used, outlet split) for funnel insights
  // Polls every 60 s to stay consistent with livePredictions refresh cadence.
  const [billData, setBillData] = useState(null);
  useEffect(() => {
    let active = true;
    function loadBillData() {
      fetch('/api/get-bill-data', { headers: { 'x-admin-key': ctx.adminToken } })
        .then(r => r.json())
        .then(d => { if (active && d.billUsage) setBillData(d); })
        .catch(() => {});
    }
    loadBillData();
    const id = setInterval(loadBillData, 60000);
    return () => { active = false; clearInterval(id); };
  }, [ctx.adminToken]);

  const billUsage   = (billData && billData.billUsage)   || [];
  const billEntries = (billData && billData.billEntries) || [];
  const outletStats = (billData && billData.outletStats) || [];

  // Ad banner performance summary (impressions/clicks by device)
  const [adStats, setAdStats] = useState(null);
  useEffect(() => {
    let active = true;
    fetch('/api/get-ad-stats', { headers: { 'x-admin-key': ctx.adminToken } })
      .then(r => r.json())
      .then(d => { if (active && d.totals) setAdStats(d); })
      .catch(() => {});
    return () => { active = false; };
  }, [ctx.adminToken]);

  const todayStart = new Date();
  todayStart.setHours(0, 0, 0, 0);
  const todayCount = preds.filter(p => p.submittedAt >= todayStart.getTime()).length;

  // ── Funnel numbers ──
  const uniquePlayers = new Set(preds.map(p => p.customerPhone).filter(Boolean)).size;
  const totalBills = billUsage.length;
  const votedBills = billUsage.filter(u => (u.used_votes || 0) > 0).length;
  const activationPct = totalBills ? Math.round((votedBills / totalBills) * 100) : 0;
  const maxVotes  = billUsage.reduce((s, u) => s + (u.max_votes  || 0), 0);
  const usedVotes = billUsage.reduce((s, u) => s + (u.used_votes || 0), 0);
  const votePct = maxVotes ? Math.round((usedVotes / maxVotes) * 100) : 0;
  const billValue = billEntries.reduce((s, e) => s + (Number(e.amount_myr) || 0), 0);

  // Repeat players: phones with 2+ predictions
  const byPhone = {};
  preds.forEach(p => { if (p.customerPhone) byPhone[p.customerPhone] = (byPhone[p.customerPhone] || 0) + 1; });
  const repeatPlayers = Object.values(byPhone).filter(n => n > 1).length;
  const repeatPct = uniquePlayers ? Math.round((repeatPlayers / uniquePlayers) * 100) : 0;

  // ── Daily series since campaign launch (Jun 10), last 14 days max ──
  const DAY = 86400000;
  const campaignStart = new Date(2026, 5, 10);
  campaignStart.setHours(0, 0, 0, 0);
  const campaignDay = Math.max(1, Math.floor((Date.now() - campaignStart.getTime()) / DAY) + 1);
  const dayCount = Math.min(14, campaignDay);
  const daily = [];
  for (let i = dayCount - 1; i >= 0; i--) {
    const dStart = todayStart.getTime() - i * DAY;
    const n = preds.filter(p => p.submittedAt >= dStart && p.submittedAt < dStart + DAY).length;
    daily.push({ label: new Date(dStart).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }), n });
  }
  const dailyPeak = Math.max(1, ...daily.map(d => d.n));

  // Momentum: last 3 days vs the 3 days before
  const last3 = daily.slice(-3).reduce((s, d) => s + d.n, 0);
  const prev3 = daily.slice(-6, -3).reduce((s, d) => s + d.n, 0);

  // ── Match engagement leaderboard ──
  const byMatch = {};
  preds.forEach(p => {
    const k = p.matchLabel || String(p.matchId);
    if (!byMatch[k]) byMatch[k] = { label: k, count: 0, id: p.matchId };
    byMatch[k].count++;
  });
  const matchBoard = Object.values(byMatch).sort((a, b) => b.count - a.count).slice(0, 6);
  const matchPeak = Math.max(1, ...matchBoard.map(m => m.count));

  // ── Crowd accuracy on settled predictions ──
  const judged = preds.filter(p => p.isCorrect === true || p.isCorrect === false);
  const correctCount = judged.filter(p => p.isCorrect === true).length;
  const accuracyPct = judged.length ? Math.round((correctCount / judged.length) * 100) : null;

  // ── Per-outlet predictions (outlet derived from bill) ──
  const predsByOutlet = {};
  preds.forEach(p => { const o = p.outlet || 'Unknown'; predsByOutlet[o] = (predsByOutlet[o] || 0) + 1; });

  // ── Per-outlet bill entries (line items, from POS) ──
  const entriesByOutlet = {};
  billEntries.forEach(e => { if (e.Shop) entriesByOutlet[e.Shop] = (entriesByOutlet[e.Shop] || 0) + 1; });

  const topOutlet = outletStats[0];
  const topOutletShare = totalBills && topOutlet ? Math.round((topOutlet.totalBills / totalBills) * 100) : 0;

  // ── Health signals with action recommendations ──
  const signals = [];
  {
    let status, note, action;
    if (last3 === 0 && prev3 === 0) {
      status = 'bad';
      note   = 'Zero predictions in the last 6 days. The campaign is stalled.';
      action = 'Run an in-store activation event or social post tied to the next match today.';
    } else if (prev3 === 0 || last3 >= prev3 * 1.1) {
      status = 'good';
      note   = `Rising: ${last3} picks last 3 days vs ${prev3} the 3 days before.`;
    } else if (last3 <= prev3 * 0.7) {
      status = 'bad';
      note   = `Falling: ${last3} picks last 3 days vs ${prev3} before.`;
      action = 'Promote the next fixture on social and brief outlet staff to verbally remind customers.';
    } else {
      status = 'warn';
      note   = `Flat: ${last3} picks last 3 days vs ${prev3} before.`;
      action = 'Leverage upcoming big matches — post match-day content to spark a spike.';
    }
    signals.push({ label: 'Momentum', value: `${last3} vs ${prev3}`, status, note, action });
  }
  if (billData) {
    const aStatus = activationPct >= 60 ? 'good' : activationPct >= 35 ? 'warn' : 'bad';
    signals.push({
      label: 'Bill activation', value: `${activationPct}%`, status: aStatus,
      note: aStatus === 'good'
        ? `${votedBills} of ${totalBills} bills cast at least one vote. Checkout handoff working.`
        : `${totalBills - votedBills} of ${totalBills} bills never voted.`,
      action: aStatus !== 'good'
        ? `Brief every cashier: hand customers the campaign QR/link with every MVR 1,000+ receipt.`
        : undefined,
    });
    const vStatus = votePct >= 50 ? 'good' : votePct >= 25 ? 'warn' : 'bad';
    signals.push({
      label: 'Vote redemption', value: `${usedVotes} / ${maxVotes} (${votePct}%)`, status: vStatus,
      note: `${maxVotes - usedVotes} earned vote slots are unspent — free re-engagement waiting.`,
      action: vStatus !== 'good'
        ? 'Send a reminder before the next kickoff: "You still have predictions to use!"'
        : undefined,
    });
    if (topOutlet) {
      const oStatus = topOutletShare <= 45 ? 'good' : topOutletShare <= 65 ? 'warn' : 'bad';
      signals.push({
        label: 'Outlet balance', value: `${topOutletShare}% from ${topOutlet.outlet}`, status: oStatus,
        note: oStatus === 'good'
          ? 'Qualifying bills spread healthily across outlets.'
          : `${topOutlet.outlet} dominates. Weaker outlets need POS material and a staff briefing.`,
        action: oStatus !== 'good'
          ? 'Send POS kits and run a brief with staff at low-performing outlets this week.'
          : undefined,
      });
    }
  }
  {
    const rStatus = repeatPct >= 40 ? 'good' : repeatPct >= 20 ? 'warn' : 'bad';
    signals.push({
      label: 'Repeat players', value: `${repeatPct}%`, status: rStatus,
      note: `${repeatPlayers} of ${uniquePlayers} players predicted more than once.`,
      action: rStatus !== 'good'
        ? 'Create pre-match hype content on social to pull existing players back for every fixture.'
        : undefined,
    });
  }
  if (accuracyPct !== null) {
    signals.push({
      label: 'Crowd accuracy', value: `${accuracyPct}%`,
      status: accuracyPct >= 50 ? 'good' : 'warn',
      note: `${correctCount} of ${judged.length} settled predictions correct. Use as social proof.`,
    });
  }

  const badCount  = signals.filter(s => s.status === 'bad').length;
  const warnCount = signals.filter(s => s.status === 'warn').length;
  const verdict = badCount >= 2 ? 'bad' : (badCount === 1 || warnCount >= 2) ? 'warn' : 'good';
  const verdictText = verdict === 'good'
    ? 'Campaign on track — engagement signals are healthy across the funnel.'
    : verdict === 'warn'
    ? 'Needs attention — some signals are lagging. See actions below.'
    : 'Off track — act on the red items below before the next match window.';

  // ── Biggest single opportunity ──
  const opportunities = [];
  if (billData && (totalBills - votedBills) > 0) {
    const avgVotes  = maxVotes / Math.max(1, totalBills);
    const untapped  = Math.round((totalBills - votedBills) * avgVotes);
    opportunities.push({
      weight: totalBills - votedBills,
      text:   `${totalBills - votedBills} bills issued but never activated → ~${untapped} predictions still unclaimed.`,
      action: 'Brief every outlet cashier to hand customers the campaign link with qualifying receipts.',
    });
  }
  if (billData && (maxVotes - usedVotes) > 3) {
    opportunities.push({
      weight: maxVotes - usedVotes,
      text:   `${maxVotes - usedVotes} earned prediction slots are sitting unused across active players.`,
      action: 'Send a WhatsApp/social reminder: "Your votes are waiting — use them before the next kickoff!"',
    });
  }
  if (outletStats.length >= 2) {
    const sorted = [...outletStats]
      .filter(o => o.outlet !== 'Unknown' && o.totalBills >= 2)
      .sort((a, b) => (a.totalBills ? a.voted / a.totalBills : 0) - (b.totalBills ? b.voted / b.totalBills : 0));
    if (sorted.length >= 2) {
      const worst   = sorted[0];
      const best    = sorted[sorted.length - 1];
      const wPct    = worst.totalBills ? Math.round((worst.voted / worst.totalBills) * 100) : 0;
      const bPct    = best.totalBills  ? Math.round((best.voted  / best.totalBills)  * 100) : 0;
      if (bPct - wPct > 20) {
        opportunities.push({
          weight: worst.notVoted,
          text:   `${worst.outlet} activation is only ${wPct}% vs ${bPct}% at ${best.outlet}.`,
          action: `Focus a staff briefing at ${worst.outlet} — ${worst.notVoted} bills there have never voted.`,
        });
      }
    }
  }
  const biggestOpportunity = opportunities.length > 0
    ? [...opportunities].sort((a, b) => b.weight - a.weight)[0]
    : null;

  const recentActivity = preds.slice(0, 6).map(p => ({
    t: p.submittedAt,
    kind: 'predict',
    text: `${p.customerName || 'Someone'} picked ${p.matchLabel} → ${p.pick}`,
    outlet: p.outlet,
  }));

  const fmt = (v) => billData ? v : '—';

  return (
    <AdminSection
      title="Campaign command centre"
      sub={`Day ${campaignDay} of the campaign · ${open.length} open · ${closed.length} awaiting result · ${settled.length} settled`}
      right={
        <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
          <AdminBtn kind="outline" icon="calendar" onClick={() => ctx.goTab('create')}>New match</AdminBtn>
          <AdminBtn kind="primary" icon="trophy" onClick={() => ctx.goTab('draw')}>Run lucky draw</AdminBtn>
        </div>
      }
    >
      {/* KPI cards: the full bill → vote → prediction funnel */}
      <div className="admin-stats" style={{
        display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 14, marginBottom: 22,
      }}>
        <StatCard label="Total predictions" value={<AnimatedCounter value={preds.length} />} accent="orange" delta={todayCount > 0 ? `+ ${todayCount} today` : undefined} />
        <StatCard label="Unique players" value={uniquePlayers.toLocaleString()} accent="ink" />
        <StatCard label="Bills issued" value={fmt(totalBills.toLocaleString())} accent="ink" />
        <StatCard label="Bill activation" value={fmt(`${activationPct}%`)} accent={activationPct >= 60 ? 'green' : 'orange'} delta={billData ? `${votedBills} voted · ${totalBills - votedBills} silent` : undefined} />
        <StatCard label="Votes used" value={fmt(`${usedVotes} / ${maxVotes}`)} accent="orange" delta={billData ? `${votePct}% redeemed` : undefined} />
        <StatCard label="Qualifying bill value" value={fmt(`MVR ${Math.round(billValue).toLocaleString()}`)} accent="grey" />
      </div>

      {/* Campaign health: funnel + opportunity + signals */}
      <Panel
        title="Campaign health"
        action={
          <span style={{
            fontSize: 11, fontWeight: 800, letterSpacing: '0.08em', textTransform: 'uppercase',
            color: '#fff', background: HEALTH_COLORS[verdict], padding: '4px 10px', borderRadius: 3,
          }}>
            {HEALTH_LABELS[verdict]}
          </span>
        }
        padding={18}
      >
        <div style={{ fontSize: 13, fontWeight: 600, color: verdict === 'bad' ? '#dc2626' : verdict === 'warn' ? '#d97706' : 'var(--success)', marginBottom: 16 }}>
          {verdictText}
        </div>

        {/* Participation funnel */}
        {billData && (
          <div style={{ marginBottom: 18, padding: '14px 16px', background: 'var(--grey-50)', borderRadius: 8, border: '1px solid var(--grey-200)' }}>
            <div style={{ fontSize: 10, letterSpacing: '0.16em', textTransform: 'uppercase', fontWeight: 800, color: 'var(--grey-500)', marginBottom: 12 }}>
              Participation funnel
            </div>
            {[
              { label: 'Bills eligible', n: totalBills,  pct: 100,         color: 'var(--ink)' },
              { label: 'Bills activated', n: votedBills, pct: activationPct, color: activationPct >= 60 ? 'var(--success)' : activationPct >= 35 ? '#d97706' : '#dc2626' },
              { label: 'Votes redeemed',  n: usedVotes,  pct: votePct,      color: votePct >= 50 ? 'var(--success)' : votePct >= 25 ? '#d97706' : '#dc2626', sub: `of ${maxVotes} earned` },
            ].map(step => (
              <div key={step.label} style={{ marginBottom: 10 }}>
                <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', marginBottom: 5 }}>
                  <span style={{ fontSize: 12, fontWeight: 600 }}>{step.label}</span>
                  <span style={{ fontSize: 12, fontWeight: 800, color: step.color, fontFamily: 'monospace' }}>
                    {step.n.toLocaleString()} <span style={{ color: 'var(--grey-400)', fontWeight: 400 }}>({step.pct}%{step.sub ? ` ${step.sub}` : ''})</span>
                  </span>
                </div>
                <div style={{ height: 9, background: 'var(--grey-200)', borderRadius: 5 }}>
                  <div style={{ height: '100%', width: `${step.pct}%`, background: step.color, borderRadius: 5, transition: 'width 0.7s ease' }} />
                </div>
              </div>
            ))}
          </div>
        )}

        {/* Biggest opportunity callout */}
        {biggestOpportunity && (
          <div style={{
            background: '#fffbeb', border: '1.5px solid #f59e0b', borderRadius: 8,
            padding: '12px 16px', marginBottom: 18, display: 'flex', gap: 12, alignItems: 'flex-start',
          }}>
            <span style={{ fontSize: 18, lineHeight: 1.2, flexShrink: 0 }}>🎯</span>
            <div>
              <div style={{ fontSize: 11, letterSpacing: '0.14em', textTransform: 'uppercase', fontWeight: 800, color: '#92400e', marginBottom: 3 }}>Biggest opportunity</div>
              <div style={{ fontSize: 13, color: 'var(--ink)', lineHeight: 1.4 }}>{biggestOpportunity.text}</div>
              <div style={{ fontSize: 12, color: '#92400e', fontWeight: 700, marginTop: 5 }}>→ {biggestOpportunity.action}</div>
            </div>
          </div>
        )}

        {/* Signal grid */}
        <div className="health-grid" style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', columnGap: 28 }}>
          {signals.map(s => <SignalRow key={s.label} {...s} />)}
        </div>
      </Panel>

      {/* Trend + match engagement */}
      <div className="admin-2col" style={{ display: 'grid', gridTemplateColumns: '1.2fr 1fr', gap: 18, marginTop: 18 }}>
        <Panel title="Daily predictions" action={<span style={{ fontSize: 11, color: 'var(--grey-500)', letterSpacing: '0.08em', textTransform: 'uppercase' }}>Since Jun 10</span>} padding={18}>
          <div style={{ display: 'flex', alignItems: 'flex-end', gap: 6, height: 130 }}>
            {daily.map(d => (
              <div key={d.label} style={{ flex: 1, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'flex-end', gap: 4, height: '100%' }}>
                <span className="font-mono" style={{ fontSize: 10, color: 'var(--grey-600)' }}>{d.n > 0 ? d.n : ''}</span>
                <div className="stripes-bg" style={{ width: '100%', height: Math.max(3, (d.n / dailyPeak) * 88) }} />
                <span style={{ fontSize: 9, color: 'var(--grey-500)', whiteSpace: 'nowrap' }}>{d.label}</span>
              </div>
            ))}
          </div>
        </Panel>

        <Panel title="Match engagement" action={<span style={{ fontSize: 11, color: 'var(--grey-500)', letterSpacing: '0.08em', textTransform: 'uppercase' }}>Top {matchBoard.length}</span>} padding={18}>
          {matchBoard.length === 0 && <div style={{ color: 'var(--grey-500)', fontSize: 13 }}>No predictions yet.</div>}
          <div style={{ display: 'grid', gap: 10 }}>
            {matchBoard.map(m => {
              const match = ctx.matchesState.find(x => x.id === m.id);
              const st = match ? match.status : '';
              return (
                <div key={m.label}>
                  <div style={{ display: 'flex', justifyContent: 'space-between', gap: 8, fontSize: 12.5, marginBottom: 4 }}>
                    <span style={{ fontWeight: 600, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
                      {m.label}
                      {st === 'SETTLED' && <span style={{ marginLeft: 6, fontSize: 10, color: 'var(--grey-500)', fontWeight: 700, letterSpacing: '0.08em' }}>SETTLED</span>}
                    </span>
                    <span className="font-mono" style={{ flexShrink: 0 }}>{m.count}</span>
                  </div>
                  <div style={{ height: 8, background: 'var(--grey-100)' }}>
                    <div className="stripes-bg" style={{ width: `${(m.count / matchPeak) * 100}%`, height: '100%' }} />
                  </div>
                </div>
              );
            })}
          </div>
        </Panel>
      </div>

      {/* Outlet performance + next match / activity */}
      <div className="admin-2col" style={{ display: 'grid', gridTemplateColumns: '1.2fr 1fr', gap: 18, marginTop: 18 }}>
        <Panel title="Outlet performance" padding={0}>
          {!billData && <div style={{ padding: '20px 18px', color: 'var(--grey-500)', fontSize: 13 }}>Loading bill data…</div>}
          {billData && (
            <div style={{ overflowX: 'auto' }}>
              <table style={{ width: '100%', borderCollapse: 'collapse', minWidth: 600 }}>
                <thead>
                  <tr style={{ background: 'var(--grey-50)' }}>
                    <Th>Outlet</Th><Th>Bills</Th><Th>Voted</Th><Th>Silent</Th><Th>Activation</Th><Th align="right">Bill entries</Th><Th align="right">Predictions</Th>
                  </tr>
                </thead>
                <tbody>
                  {(() => {
                    // Merge outletStats (from bill_usage) with outlets only in bill_entries
                    // so outlets with entries but zero activations still appear.
                    const statsNames = new Set(outletStats.map(o => o.outlet));
                    const extraOutlets = Object.keys(entriesByOutlet)
                      .filter(shop => !statsNames.has(shop))
                      .map(shop => ({ outlet: shop, totalBills: 0, voted: 0, notVoted: 0 }));
                    const allOutletRows = [...outletStats, ...extraOutlets];
                    if (allOutletRows.length === 0) {
                      return <tr><td colSpan="7" style={{ padding: 24, textAlign: 'center', color: 'var(--grey-500)', fontSize: 13 }}>No bill data yet.</td></tr>;
                    }
                    return allOutletRows.map(o => {
                      const pct = o.totalBills ? Math.round((o.voted / o.totalBills) * 100) : 0;
                      return (
                        <tr key={o.outlet} style={{ borderTop: '1px solid var(--grey-100)' }}>
                          <Td><span style={{ fontWeight: 600, fontSize: 13 }}>{o.outlet}</span></Td>
                          <Td><span className="font-mono" style={{ fontSize: 12.5 }}>{o.totalBills}</span></Td>
                          <Td><span className="font-mono" style={{ fontSize: 12.5, color: 'var(--success)' }}>{o.voted}</span></Td>
                          <Td><span className="font-mono" style={{ fontSize: 12.5, color: 'var(--orange)' }}>{o.notVoted}</span></Td>
                          <Td>
                            <span className="font-mono" style={{ fontSize: 12.5, fontWeight: 700, color: o.totalBills === 0 ? 'var(--grey-400)' : pct >= 60 ? 'var(--success)' : pct >= 35 ? '#d97706' : '#dc2626' }}>
                              {o.totalBills === 0 ? '—' : `${pct}%`}
                            </span>
                          </Td>
                          <Td align="right"><span className="font-mono" style={{ fontSize: 12.5 }}>{entriesByOutlet[o.outlet] || 0}</span></Td>
                          <Td align="right"><span className="font-mono" style={{ fontSize: 12.5 }}>{predsByOutlet[o.outlet] || 0}</span></Td>
                        </tr>
                      );
                    });
                  })()}
                </tbody>
                {outletStats.length > 0 && (() => {
                  const totalVoted    = outletStats.reduce((s, o) => s + o.voted, 0);
                  const totalNotVoted = outletStats.reduce((s, o) => s + o.notVoted, 0);
                  const totalEntries  = billEntries.length;
                  const totalPreds    = preds.length;
                  const totalPct      = totalBills ? Math.round((totalVoted / totalBills) * 100) : 0;
                  return (
                    <tfoot>
                      <tr style={{ borderTop: '2px solid var(--grey-200)', background: 'var(--grey-50)' }}>
                        <Td><span style={{ fontWeight: 800, fontSize: 13 }}>Total</span></Td>
                        <Td><span className="font-mono" style={{ fontSize: 12.5, fontWeight: 700 }}>{totalBills}</span></Td>
                        <Td><span className="font-mono" style={{ fontSize: 12.5, fontWeight: 700, color: 'var(--success)' }}>{totalVoted}</span></Td>
                        <Td><span className="font-mono" style={{ fontSize: 12.5, fontWeight: 700, color: 'var(--orange)' }}>{totalNotVoted}</span></Td>
                        <Td>
                          <span className="font-mono" style={{ fontSize: 12.5, fontWeight: 800, color: totalPct >= 60 ? 'var(--success)' : totalPct >= 35 ? '#d97706' : '#dc2626' }}>
                            {totalPct}%
                          </span>
                        </Td>
                        <Td align="right"><span className="font-mono" style={{ fontSize: 12.5, fontWeight: 700 }}>{totalEntries}</span></Td>
                        <Td align="right"><span className="font-mono" style={{ fontSize: 12.5, fontWeight: 700 }}>{totalPreds}</span></Td>
                      </tr>
                    </tfoot>
                  );
                })()}
              </table>
            </div>
          )}
        </Panel>

        <div style={{ display: 'grid', gap: 18, alignContent: 'start' }}>
          <Panel title="Next match" padding={18}>
            {(() => {
              const next = [...ctx.matchesState]
                .filter(m => m.status !== 'SETTLED' && m.status !== 'CLOSED')
                .sort((a, b) => a.kickoffMs - b.kickoffMs)[0];
              if (!next) return <div style={{ color: 'var(--grey-500)' }}>No upcoming matches.</div>;
              return (
                <div>
                  <div style={{ display: 'flex', alignItems: 'center', gap: 10, justifyContent: 'space-between' }}>
                    <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                      <Flag code={next.teamA.flag} w={26} h={17} />
                      <div className="font-display" style={{ fontSize: 14 }}>{next.teamA.name}</div>
                    </div>
                    <span className="font-mono" style={{ fontSize: 11, color: 'var(--grey-500)' }}>vs</span>
                    <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                      <div className="font-display" style={{ fontSize: 14 }}>{next.teamB.name}</div>
                      <Flag code={next.teamB.flag} w={26} h={17} />
                    </div>
                  </div>
                  <div style={{ marginTop: 14, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
                    <div style={{ fontSize: 11, letterSpacing: '0.14em', textTransform: 'uppercase', color: 'var(--grey-500)', fontWeight: 700 }}>
                      Kicks off in
                    </div>
                    <Countdown to={next.kickoffMs} tone="ink" />
                  </div>
                </div>
              );
            })()}
          </Panel>

          <Panel
            title="Recent activity"
            action={<span style={{ fontSize: 11, color: 'var(--grey-500)', letterSpacing: '0.08em', textTransform: 'uppercase' }}>Latest</span>}
            padding={0}
          >
            <ul style={{ margin: 0, padding: 0, listStyle: 'none' }}>
              {recentActivity.length === 0 && (
                <li style={{ padding: '24px 18px', textAlign: 'center', color: 'var(--grey-500)', fontSize: 13 }}>
                  {ctx.predictionsLoading ? 'Loading activity…' : 'No activity yet. Predictions will appear here as customers submit picks.'}
                </li>
              )}
              {recentActivity.map((a, i) => (
                <li key={i} style={{
                  display: 'flex', alignItems: 'flex-start', gap: 14,
                  padding: '12px 18px',
                  borderBottom: i < recentActivity.length - 1 ? '1px solid var(--grey-100)' : 'none',
                }}>
                  <div style={{
                    width: 32, height: 32, flexShrink: 0,
                    background: 'var(--orange-50)', color: 'var(--orange-dark)',
                    display: 'flex', alignItems: 'center', justifyContent: 'center',
                  }}>
                    <Icon name="check" size={15} />
                  </div>
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <div style={{ fontSize: 13, color: 'var(--ink)' }}>{a.text}</div>
                    <div style={{ fontSize: 11.5, color: 'var(--grey-500)', marginTop: 2, display: 'flex', gap: 10 }}>
                      <span>{timeAgo(a.t)}</span>
                      {a.outlet && <span>· {a.outlet}</span>}
                    </div>
                  </div>
                </li>
              ))}
            </ul>
          </Panel>
        </div>
      </div>

      {/* Advertising performance snapshot */}
      <div style={{ marginTop: 18 }}>
        <Panel
          title="Advertising performance"
          action={
            <button onClick={() => ctx.goTab('advertising')} style={{
              background: 'none', border: 'none', cursor: 'pointer',
              fontSize: 11, letterSpacing: '0.08em', textTransform: 'uppercase',
              color: 'var(--orange)', fontWeight: 700,
            }}>
              View Advertising →
            </button>
          }
          padding={18}
        >
          {!adStats && <div style={{ color: 'var(--grey-500)', fontSize: 13 }}>Loading ad stats…</div>}
          {adStats && (() => {
            const { totals } = adStats;
            const totalImpressions = totals.desktop.impressions + totals.mobile.impressions;
            const totalClicks = totals.desktop.clicks + totals.mobile.clicks;
            const overallCtr = totalImpressions ? (totalClicks / totalImpressions) * 100 : 0;
            const desktopCtr = totals.desktop.impressions ? (totals.desktop.clicks / totals.desktop.impressions) * 100 : 0;
            const mobileCtr  = totals.mobile.impressions  ? (totals.mobile.clicks  / totals.mobile.impressions)  * 100 : 0;
            return (
              <div className="admin-stats" style={{ display: 'grid', gridTemplateColumns: 'repeat(5, 1fr)', gap: 14 }}>
                <StatCard label="Ad impressions" value={totalImpressions.toLocaleString()} accent="ink" />
                <StatCard label="Ad clicks" value={totalClicks.toLocaleString()} accent="orange" />
                <StatCard label="Overall CTR" value={`${overallCtr.toFixed(2)}%`} accent="green" />
                <StatCard label="Desktop CTR" value={`${desktopCtr.toFixed(2)}%`} accent="ink" delta={`${totals.desktop.impressions.toLocaleString()} impr`} />
                <StatCard label="Mobile CTR" value={`${mobileCtr.toFixed(2)}%`} accent="ink" delta={`${totals.mobile.impressions.toLocaleString()} impr`} />
              </div>
            );
          })()}
        </Panel>
      </div>

      <style>{`
        @media (max-width: 900px) {
          .admin-stats { grid-template-columns: 1fr 1fr !important; }
          .admin-2col  { grid-template-columns: 1fr !important; }
          .health-grid { grid-template-columns: 1fr !important; }
        }
      `}</style>
    </AdminSection>
  );
}

/* ============================================================
   MATCHES LIST
   ============================================================ */
function AdminMatches({ ctx }) {
  const [q, setQ] = useState('');
  const [filter, setFilter] = useState('ALL');

  const filtered = ctx.matchesState.filter(m => {
    if (filter !== 'ALL' && m.status !== filter) return false;
    if (q) {
      const Q = q.toLowerCase();
      return (m.teamA.name + ' ' + m.teamB.name + ' ' + m.stage + ' ' + m.venue).toLowerCase().includes(Q);
    }
    return true;
  });

  const filters = ['ALL', 'OPEN', 'SCHEDULED', 'CLOSED', 'SETTLED'];
  const counts = filters.reduce((acc, f) => {
    acc[f] = f === 'ALL' ? ctx.matchesState.length : ctx.matchesState.filter(m => m.status === f).length;
    return acc;
  }, {});

  function fakeCsv(label) {
    ctx.setToast({ message: `CSV "${label}" queued for download.`, tone: 'info' });
  }
  function deleteMatch(id) {
    ctx.setMatchesState(s => s.filter(m => m.id !== id));
    ctx.setToast({ message: 'Match removed.', tone: 'success' });
  }

  return (
    <AdminSection
      title="All matches"
      sub="Schedule, settle, run draws, or export per-match prediction CSVs."
      right={
        <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
          <AdminBtn kind="outline" icon="receipt" onClick={() => fakeCsv('all_predictions.csv')}>Export all predictions</AdminBtn>
          <AdminBtn kind="primary" icon="calendar" onClick={() => ctx.goTab('create')}>Schedule new</AdminBtn>
        </div>
      }
    >
      <Panel padding={0}>
        {/* Filter bar */}
        <div style={{
          padding: '14px 18px',
          display: 'flex', gap: 14, flexWrap: 'wrap', alignItems: 'center',
          borderBottom: '1px solid var(--grey-200)',
        }}>
          <div style={{ position: 'relative', flex: '1 1 240px', maxWidth: 320 }}>
            <input value={q} onChange={(e) => setQ(e.target.value)} placeholder="Search by team, stage, venue..."
              style={{ ...inputStyle, paddingLeft: 36 }} />
            <svg style={{ position: 'absolute', left: 11, top: '50%', transform: 'translateY(-50%)' }}
              width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="var(--grey-400)" strokeWidth="2" strokeLinecap="round">
              <circle cx="11" cy="11" r="7"/><path d="M21 21l-4-4"/>
            </svg>
          </div>
          <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>
            {filters.map(f => {
              const active = filter === f;
              return (
                <button key={f} onClick={() => setFilter(f)} style={{
                  background: active ? 'var(--ink)' : 'transparent',
                  color: active ? '#fff' : 'var(--grey-700)',
                  border: `1.5px solid ${active ? 'var(--ink)' : 'var(--grey-300)'}`,
                  padding: '7px 12px',
                  fontSize: 11.5, letterSpacing: '0.06em', textTransform: 'uppercase', fontWeight: 700,
                  cursor: 'pointer',
                  display: 'inline-flex', alignItems: 'center', gap: 6,
                }}>
                  {f}
                  <span style={{
                    background: active ? 'var(--orange)' : 'var(--grey-100)',
                    color: active ? '#fff' : 'var(--grey-600)',
                    padding: '1px 6px', fontSize: 10, borderRadius: 999,
                  }}>{counts[f]}</span>
                </button>
              );
            })}
          </div>
        </div>

        {/* Table */}
        <div style={{ overflowX: 'auto' }}>
          <table style={{ width: '100%', borderCollapse: 'collapse', minWidth: 880 }}>
            <thead>
              <tr style={{ background: 'var(--grey-50)' }}>
                <Th>Match</Th>
                <Th>Stage</Th>
                <Th>Kickoff</Th>
                <Th>Status</Th>
                <Th align="right">Predictions</Th>
                <Th align="right">Actions</Th>
              </tr>
            </thead>
            <tbody>
              {filtered.map(m => {
                const predCount = ctx.livePredictions.filter(p => p.matchId === m.id).length;
                return (
                  <tr key={m.id} style={{ borderTop: '1px solid var(--grey-100)' }}>
                    <Td>
                      <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                        <Flag code={m.teamA.flag} w={24} h={16} />
                        <span style={{ fontWeight: 700, fontSize: 13.5 }}>{m.teamA.name}</span>
                        <span style={{ color: 'var(--grey-400)', fontSize: 11 }}>vs</span>
                        <span style={{ fontWeight: 700, fontSize: 13.5 }}>{m.teamB.name}</span>
                        <Flag code={m.teamB.flag} w={24} h={16} />
                      </div>
                      <div style={{ fontSize: 11.5, color: 'var(--grey-500)', marginTop: 4 }}>{m.venue}</div>
                    </Td>
                    <Td><span style={{ fontSize: 12.5 }}>{m.stage}</span></Td>
                    <Td>
                      <div style={{ fontSize: 12.5 }}>{formatKickoff(m.kickoffAt)}</div>
                      {m.status === 'OPEN' && (
                        <div style={{ marginTop: 4 }}>
                          <Countdown to={m.kickoffMs} tone="light" size="sm" prefix="CLOSES" />
                        </div>
                      )}
                    </Td>
                    <Td><StatusBadge status={m.status} size="sm" /></Td>
                    <Td align="right">
                      <span className="font-display" style={{ fontSize: 18 }}>{predCount.toLocaleString()}</span>
                    </Td>
                    <Td align="right">
                      <div style={{ display: 'inline-flex', gap: 6 }}>
                        <IconBtn title="Download CSV" onClick={() => fakeCsv(`match_${m.id}_predictions.csv`)} icon="receipt" />
                        {m.status === 'CLOSED' && (
                          <IconBtn title="Settle" onClick={() => ctx.goTab('settle')} icon="check" tone="orange" />
                        )}
                        {m.status === 'SETTLED' && (
                          <IconBtn title="Run draw" onClick={() => ctx.goTab('draw')} icon="trophy" tone="orange" />
                        )}
                        <IconBtn title="Delete" onClick={() => deleteMatch(m.id)} icon="bolt" tone="danger" />
                      </div>
                    </Td>
                  </tr>
                );
              })}
              {filtered.length === 0 && (
                <tr><td colSpan="6" style={{ padding: 32, textAlign: 'center', color: 'var(--grey-500)' }}>No matches match those filters.</td></tr>
              )}
            </tbody>
          </table>
        </div>
      </Panel>
    </AdminSection>
  );
}

function Th({ children, align = 'left' }) {
  return (
    <th style={{
      padding: '12px 14px', textAlign: align,
      fontSize: 11, letterSpacing: '0.12em', textTransform: 'uppercase',
      color: 'var(--grey-600)', fontWeight: 700,
    }}>{children}</th>
  );
}
function Td({ children, align = 'left', style: extraStyle }) {
  return <td style={{ padding: '14px', textAlign: align, verticalAlign: 'middle', ...extraStyle }}>{children}</td>;
}
function IconBtn({ icon, title, onClick, tone = 'default' }) {
  const palette = {
    default: { bg: '#fff',          fg: 'var(--grey-700)', bd: 'var(--grey-300)' },
    orange:  { bg: 'var(--orange-50)', fg: 'var(--orange-dark)', bd: 'var(--orange)' },
    danger:  { bg: '#fff',          fg: '#b91c1c',         bd: '#fecaca' },
  }[tone];
  return (
    <button onClick={onClick} title={title} style={{
      background: palette.bg, color: palette.fg, border: `1.5px solid ${palette.bd}`,
      padding: 7, cursor: 'pointer',
      display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
    }}>
      <Icon name={icon} size={15} />
    </button>
  );
}

/* ============================================================
   CREATE MATCH
   ============================================================ */
function AdminCreate({ ctx }) {
  const teamKeys = Object.keys(TEAMS);
  const [teamA, setTeamA] = useState('');
  const [teamB, setTeamB] = useState('');
  const [kickoff, setKickoff] = useState('');
  const [stage, setStage] = useState('Group Stage');
  const [venue, setVenue] = useState('');

  const ok = teamA && teamB && teamA !== teamB && kickoff;

  function submit() {
    if (!ok) return;
    const ko = new Date(kickoff);
    const newMatch = {
      id: Math.max(...ctx.matchesState.map(m => m.id)) + 1,
      teamA: TEAMS[teamA], teamB: TEAMS[teamB],
      teamAKey: teamA, teamBKey: teamB,
      kickoffAt: ko, kickoffMs: ko.getTime(),
      stage, venue: venue || '-',
      status: ko.getTime() > Date.now() ? 'OPEN' : 'CLOSED',
      settled: null,
    };
    ctx.setMatchesState(s => [...s, newMatch]);
    ctx.setToast({ message: `Match created: ${newMatch.teamA.name} vs ${newMatch.teamB.name}`, tone: 'success' });
    setTeamA(''); setTeamB(''); setKickoff(''); setStage('Group Stage'); setVenue('');
  }

  return (
    <AdminSection
      title="Schedule a new match"
      sub="Pick the two teams, set the kickoff, and choose the stage. Predictions auto-open 24h before kickoff."
    >
      <div className="admin-2col" style={{ display: 'grid', gridTemplateColumns: 'minmax(0, 1fr) minmax(0, 1fr)', gap: 18 }}>
        <Panel title="Match details">
          <div style={{ display: 'grid', gap: 16 }}>
            <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
              <FormSelect label="Team A" value={teamA} onChange={setTeamA} options={teamKeys.map(k => ({ value: k, label: TEAMS[k].name }))} placeholder="Select team A" />
              <FormSelect label="Team B" value={teamB} onChange={setTeamB} options={teamKeys.filter(k => k !== teamA).map(k => ({ value: k, label: TEAMS[k].name }))} placeholder="Select team B" />
            </div>
            <div>
              <FormLabel>Kickoff (your local time)</FormLabel>
              <input type="datetime-local" value={kickoff} onChange={(e) => setKickoff(e.target.value)} style={inputStyle} />
            </div>
            <FormSelect label="Stage" value={stage} onChange={setStage} options={STAGES.map(s => ({ value: s, label: s }))} />
            <div>
              <FormLabel>Venue <span style={{ color: 'var(--grey-400)', fontWeight: 400 }}>(optional)</span></FormLabel>
              <input value={venue} onChange={(e) => setVenue(e.target.value)} placeholder="e.g. MetLife Stadium, New Jersey" style={inputStyle} />
            </div>
            <div style={{ display: 'flex', gap: 10, marginTop: 6 }}>
              <AdminBtn kind="primary" icon="check" onClick={submit} disabled={!ok}>Create match</AdminBtn>
              <AdminBtn kind="ghost" onClick={() => { setTeamA(''); setTeamB(''); setKickoff(''); setVenue(''); }}>Reset</AdminBtn>
            </div>
          </div>
        </Panel>

        <Panel title="Preview" action={<span style={{ fontSize: 11, color: 'var(--grey-500)', letterSpacing: '0.06em', textTransform: 'uppercase' }}>How it'll look</span>}>
          {(teamA && teamB) ? (
            <div style={{
              border: '2px solid var(--grey-200)', padding: 18, background: 'var(--grey-50)',
            }}>
              <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 14 }}>
                <StatusBadge status="OPEN" size="sm" />
                <span className="font-mono" style={{ fontSize: 11, color: 'var(--grey-500)' }}>{stage}</span>
              </div>
              <div style={{ display: 'grid', gridTemplateColumns: '1fr auto 1fr', alignItems: 'center', gap: 14 }}>
                <div style={{ textAlign: 'center' }}>
                  <Flag code={TEAMS[teamA].flag} w={48} h={32} />
                  <div className="font-display" style={{ fontSize: 14, marginTop: 6 }}>{TEAMS[teamA].name}</div>
                </div>
                <VSHex size={32} />
                <div style={{ textAlign: 'center' }}>
                  <Flag code={TEAMS[teamB].flag} w={48} h={32} />
                  <div className="font-display" style={{ fontSize: 14, marginTop: 6 }}>{TEAMS[teamB].name}</div>
                </div>
              </div>
              <div style={{ marginTop: 14, fontSize: 12, color: 'var(--grey-600)' }}>
                {kickoff ? formatKickoff(new Date(kickoff)) : 'Set a kickoff time...'}
                {venue && <> · {venue}</>}
              </div>
            </div>
          ) : (
            <div style={{ color: 'var(--grey-500)', fontSize: 13.5, padding: 20, textAlign: 'center' }}>
              Pick two teams to see the preview.
            </div>
          )}
        </Panel>
      </div>

      <style>{`
        @media (max-width: 900px) {
          .admin-2col { grid-template-columns: 1fr !important; }
        }
      `}</style>
    </AdminSection>
  );
}

function FormLabel({ children }) {
  return <div style={{ fontSize: 11, letterSpacing: '0.14em', textTransform: 'uppercase', fontWeight: 700, marginBottom: 6 }}>{children}</div>;
}
function FormSelect({ label, value, onChange, options, placeholder }) {
  return (
    <div>
      <FormLabel>{label}</FormLabel>
      <select value={value} onChange={(e) => onChange(e.target.value)} style={inputStyle}>
        {placeholder && <option value="">- {placeholder} -</option>}
        {options.map(o => <option key={o.value} value={o.value}>{o.label}</option>)}
      </select>
    </div>
  );
}

/* ============================================================
   SETTLE
   ============================================================ */
function AdminSettle({ ctx }) {
  const candidates = ctx.matchesState.filter(m => m.status === 'CLOSED' || m.status === 'OPEN');
  const [mid, setMid] = useState('');
  const [result, setResult] = useState(null); // 'A' | 'DRAW' | 'B'
  const [scoreA, setScoreA] = useState('');
  const [scoreB, setScoreB] = useState('');
  const match = candidates.find(m => m.id === Number(mid));

  const [saving, setSaving] = useState(false);

  async function settle() {
    if (!match || !result || saving) return;
    const winner = result === 'A' ? match.teamA.name : result === 'B' ? match.teamB.name : 'Draw';
    setSaving(true);
    try {
      const r = await fetch('/api/settle-match', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json', 'x-admin-key': ctx.adminToken },
        body: JSON.stringify({
          match_id:    match.id,
          match_label: `${match.teamA.name} vs ${match.teamB.name}`,
          winner_pick: result,
          score_a:     Number(scoreA) || 0,
          score_b:     Number(scoreB) || 0,
        }),
      });
      const data = await r.json();
      if (!r.ok) {
        ctx.setToast({ message: data.error || 'Could not settle match.', tone: 'error' });
        setSaving(false);
        return;
      }
      // Update local view
      ctx.setMatchesState(s => s.map(m => m.id === match.id ? {
        ...m, status: 'SETTLED',
        settled: {
          winner: result === 'A' ? match.teamAKey : result === 'B' ? match.teamBKey : null,
          scoreA: Number(scoreA) || 0, scoreB: Number(scoreB) || 0,
          draw: result === 'DRAW',
        }
      } : m));
      const g = data.graded || {};
      ctx.setToast({
        message: `Settled: ${match.teamA.name} ${scoreA || 0}-${scoreB || 0} ${match.teamB.name} · ${winner} · ${g.correct || 0}/${g.total || 0} correct`,
        tone: 'success',
      });
      setMid(''); setResult(null); setScoreA(''); setScoreB('');
    } catch (e) {
      ctx.setToast({ message: 'Network error. Try again.', tone: 'error' });
    }
    setSaving(false);
  }

  // ── Auto-sync results from FIFA via Firecrawl ──
  const [syncing, setSyncing] = useState(false);
  async function syncFifa() {
    if (syncing) return;
    setSyncing(true);
    try {
      const r = await fetch('/api/sync-fifa-results', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json', 'x-admin-key': ctx.adminToken },
      });
      const d = await r.json();
      if (!r.ok) {
        ctx.setToast({ message: d.error || 'FIFA sync failed.', tone: 'error' });
      } else if (d.settledCount > 0) {
        ctx.setToast({ message: `FIFA sync: ${d.settledCount} match(es) auto-settled from live results.`, tone: 'success' });
      } else {
        ctx.setToast({ message: `FIFA sync done. No newly-finished matches found (${d.finishedCount} finished total).`, tone: 'info' });
      }
    } catch (e) {
      ctx.setToast({ message: 'FIFA sync network error.', tone: 'error' });
    }
    setSyncing(false);
  }

  return (
    <AdminSection
      title="Settle a match"
      sub="Pull live results from FIFA automatically, or enter a result manually. Predictions auto-grade and unlock the lucky-draw."
    >
      <Panel style={{ marginBottom: 16 }}>
        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 14, flexWrap: 'wrap' }}>
          <div>
            <div className="font-display" style={{ fontSize: 15, color: 'var(--ink)' }}>Auto-settle from FIFA</div>
            <div style={{ fontSize: 12.5, color: 'var(--grey-500)', marginTop: 3 }}>
              Scrapes fifa.com live scores and settles every finished match in one click.
            </div>
          </div>
          <AdminBtn kind="primary" icon="bolt" disabled={syncing} onClick={syncFifa}>
            {syncing ? 'Syncing from FIFA...' : 'Sync results from FIFA'}
          </AdminBtn>
        </div>
      </Panel>
      <Panel>
        {candidates.length === 0 ? (
          <div style={{ color: 'var(--grey-500)', padding: 20, textAlign: 'center' }}>
            No matches awaiting a result.
          </div>
        ) : (
          <>
            <div style={{ maxWidth: 480 }}>
              <FormSelect label="Pick a match" value={mid} onChange={setMid} placeholder="Select"
                options={candidates.map(m => ({
                  value: String(m.id),
                  label: `${m.teamA.name} vs ${m.teamB.name} · ${m.stage} · ${m.status}`,
                }))}
              />
            </div>

            {match && (
              <div style={{ marginTop: 22 }}>
                <FormLabel>Final result</FormLabel>
                <div className="settle-grid" style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 12 }}>
                  <SettleBtn selected={result === 'A'} onClick={() => setResult('A')} kind="team" flag={match.teamA.flag} label={`${match.teamA.name} wins`} />
                  <SettleBtn selected={result === 'DRAW'} onClick={() => setResult('DRAW')} kind="draw" label="Draw" />
                  <SettleBtn selected={result === 'B'} onClick={() => setResult('B')} kind="team" flag={match.teamB.flag} label={`${match.teamB.name} wins`} />
                </div>

                <div style={{ marginTop: 22, display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16, maxWidth: 360 }}>
                  <div>
                    <FormLabel>{match.teamA.name} score</FormLabel>
                    <input type="number" min="0" value={scoreA} onChange={(e) => setScoreA(e.target.value)} className="font-mono" style={inputStyle} />
                  </div>
                  <div>
                    <FormLabel>{match.teamB.name} score</FormLabel>
                    <input type="number" min="0" value={scoreB} onChange={(e) => setScoreB(e.target.value)} className="font-mono" style={inputStyle} />
                  </div>
                </div>

                <div style={{ marginTop: 24, display: 'flex', gap: 10, flexWrap: 'wrap' }}>
                  <AdminBtn kind="primary" icon="check" disabled={!result || saving} onClick={settle}>{saving ? 'Settling...' : 'Mark match settled'}</AdminBtn>
                  <AdminBtn kind="ghost" onClick={() => { setMid(''); setResult(null); }}>Cancel</AdminBtn>
                </div>

                <div style={{
                  marginTop: 18, padding: '12px 14px', background: 'var(--orange-50)', border: '1.5px solid var(--orange-100)',
                  fontSize: 12.5, color: 'var(--ink)',
                }}>
                  ⚠ This action is irreversible. It will flag <strong>{ctx.livePredictions.filter(p => p.matchId === match.id).length}</strong> predictions
                  as correct / incorrect and unlock the lucky draw.
                </div>
              </div>
            )}
          </>
        )}
      </Panel>

      <style>{`
        @media (max-width: 700px) {
          .settle-grid { grid-template-columns: 1fr !important; }
        }
      `}</style>
    </AdminSection>
  );
}

function SettleBtn({ selected, onClick, kind, flag, label }) {
  return (
    <button onClick={onClick} style={{
      background: selected ? 'var(--orange-50)' : '#fff',
      border: selected ? '2px solid var(--orange)' : '1px solid var(--grey-200)',
      borderRadius: 10,
      boxShadow: selected ? '0 4px 16px rgba(241,86,35,0.22)' : '0 2px 8px rgba(17,6,24,0.05)',
      padding: '18px 14px',
      display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 10,
      cursor: 'pointer',
    }}>
      {kind === 'draw' ? (
        <div style={{ width: 56, height: 40, background: 'var(--ink)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
          <Icon name="scale" size={22} color="var(--orange)" />
        </div>
      ) : (
        <Flag code={flag} w={48} h={32} />
      )}
      <span className="font-display" style={{ fontSize: 13.5, textAlign: 'center' }}>{label}</span>
    </button>
  );
}

/* ============================================================
   DRAW
   ============================================================ */
function AdminDraw({ ctx }) {
  /* ── prize-type config ── */
  const PRIZE_TYPES = [
    { key: 'matchday', label: 'Match Day',  slots: 2, icon: 'bolt',    color: '#f97316', sub: '2 winners per match day · all participants' },
    { key: 'weekly',   label: 'Weekly',     slots: 1, icon: 'trophy',  color: '#7c3aed', sub: '1 winner per week · all participants' },
    { key: 'campaign', label: 'Campaign',   slots: 1, icon: 'star',    color: '#16a34a', sub: '1 grand winner · correct predictions only' },
  ];

  const [prizeType, setPrizeType]         = useState('matchday');
  const [scope, setScope]                 = useState('');
  const [searchQ, setSearchQ]             = useState('');
  const [rolling, setRolling]             = useState(false);
  const [candidate, setCandidate]         = useState(null);
  const [savingWinner, setSavingWinner]   = useState(false);
  const [finalWinners, setFinalWinners]   = useState([]);
  const [loadingFinal, setLoadingFinal]   = useState(true);
  const [rollTick, setRollTick]           = useState(0);  // animation key

  /* ── load finalized winners ── */
  useEffect(() => {
    setLoadingFinal(true);
    fetch('/api/draw-winners', { headers: { 'x-admin-key': ctx.adminToken } })
      .then(r => r.json())
      .then(d => { setFinalWinners(Array.isArray(d) ? d : []); setLoadingFinal(false); })
      .catch(() => setLoadingFinal(false));
  }, [ctx.adminToken]);

  /* ── reset scope & candidate when prize type changes ── */
  useEffect(() => { setScope(''); setCandidate(null); setSearchQ(''); }, [prizeType]);

  /* ── matchday options (group by MVT date = UTC+5) ── */
  const matchdayOptions = useMemo(() => {
    const DAYS = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'];
    const MONTHS = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
    const byDate = {};
    ctx.matchesState.forEach(m => {
      const mvt = new Date(new Date(m.kickoffAt || m.kickoff).getTime() + 5 * 3600 * 1000);
      const key = mvt.toISOString().slice(0, 10);
      (byDate[key] || (byDate[key] = [])).push(m.id);
    });
    return Object.keys(byDate).sort().map((key, i) => {
      const d = new Date(key + 'T00:00:00Z');
      return {
        value: key,
        label: `Matchday ${i + 1} · ${DAYS[d.getUTCDay()]} ${d.getUTCDate()} ${MONTHS[d.getUTCMonth()]}`,
        matchIds: byDate[key],
      };
    });
  }, [ctx.matchesState]);

  /* ── weekly options (fixed campaign weeks) ── */
  const weekOptions = useMemo(() => {
    // Fixed week boundaries (MVT dates, inclusive)
    const FIXED_WEEKS = [
      { value: 'week-1', label: 'Week 1 · Jun 10 – Jun 20', start: '2026-06-10', end: '2026-06-20' },
      { value: 'week-2', label: 'Week 2 · Jun 21 – Jun 27', start: '2026-06-21', end: '2026-06-27' },
      { value: 'week-3', label: 'Week 3 · Jun 28 – Jul 4',  start: '2026-06-28', end: '2026-07-04' },
      { value: 'week-4', label: 'Week 4 · Jul 5 – Jul 11',  start: '2026-07-05', end: '2026-07-11' },
      { value: 'week-5', label: 'Week 5 · Jul 12 – Jul 18', start: '2026-07-12', end: '2026-07-18' },
      { value: 'week-6', label: 'Week 6 · Jul 19 – Jul 20', start: '2026-07-19', end: '2026-07-20' },
    ];
    return FIXED_WEEKS.map(wk => {
      const startMs = new Date(wk.start + 'T00:00:00Z').getTime();
      const endMs   = new Date(wk.end   + 'T23:59:59Z').getTime();
      const matchIds = ctx.matchesState
        .filter(m => {
          const mvtMs = new Date(m.kickoffAt || m.kickoff).getTime() + 5 * 3600 * 1000;
          return mvtMs >= startMs && mvtMs <= endMs;
        })
        .map(m => m.id);
      return { value: wk.value, label: wk.label, matchIds };
    }).filter(wk => wk.matchIds.length > 0);
  }, [ctx.matchesState]);

  /* ── match ID set for current scope ── */
  const scopeMatchIds = useMemo(() => {
    if (prizeType === 'matchday') {
      const opt = matchdayOptions.find(o => o.value === scope);
      return new Set(opt ? opt.matchIds : []);
    }
    if (prizeType === 'weekly') {
      const opt = weekOptions.find(o => o.value === scope);
      return new Set(opt ? opt.matchIds : []);
    }
    return null; // campaign = all
  }, [prizeType, scope, matchdayOptions, weekOptions]);

  /* ── draw pool: unique participants for scope ── */
  const pool = useMemo(() => {
    if (!ctx.livePredictions) return [];
    const seen = {};
    const preds = ctx.livePredictions.filter(p => {
      if (p.isVoid) return false;
      if (prizeType === 'campaign') return p.isCorrect === true;
      if (scopeMatchIds && !scopeMatchIds.has(p.matchId)) return false;
      return true;
    });
    preds.forEach(p => {
      if (!seen[p.customerPhone]) {
        seen[p.customerPhone] = { name: p.customerName, phone: p.customerPhone, billNumber: p.billNumber, outlet: p.outlet || '—', count: 0 };
      }
      seen[p.customerPhone].count++;
    });
    return Object.values(seen).sort((a, b) => a.name.localeCompare(b.name));
  }, [prizeType, scope, scopeMatchIds, ctx.livePredictions]);

  /* ── phones already confirmed as winners for this scope ── */
  const confirmedPhonesForScope = useMemo(() => {
    const scopeK = prizeType === 'campaign' ? 'campaign' : scope;
    return new Set(finalWinners.filter(w => w.scope_key === scopeK && w.prize_type === prizeType).map(w => w.winner_phone));
  }, [finalWinners, prizeType, scope]);

  /* ── eligible pool (exclude already-confirmed winners) ── */
  const eligiblePool = useMemo(() => pool.filter(p => !confirmedPhonesForScope.has(p.phone)), [pool, confirmedPhonesForScope]);

  /* ── filtered pool for display ── */
  const displayPool = useMemo(() => {
    const q = searchQ.toLowerCase();
    return pool.filter(p => !q || p.name.toLowerCase().includes(q) || p.phone.includes(q) || p.billNumber.toLowerCase().includes(q));
  }, [pool, searchQ]);

  /* ── slots state ── */
  const ptConfig = PRIZE_TYPES.find(t => t.key === prizeType);
  const scopeK = prizeType === 'campaign' ? 'campaign' : scope;
  const slotsFilled = finalWinners.filter(w => w.prize_type === prizeType && w.scope_key === scopeK).length;
  const nextSlot    = slotsFilled + 1;
  const allSlotsFilled = slotsFilled >= ptConfig.slots;

  /* ── run draw ── */
  function runDraw() {
    if (!eligiblePool.length || allSlotsFilled) return;
    setRolling(true);
    setCandidate(null);
    setRollTick(t => t + 1);
    setTimeout(() => {
      const winner = eligiblePool[Math.floor(Math.random() * eligiblePool.length)];
      setCandidate(winner);
      setRolling(false);
    }, 1800);
  }

  /* ── confirm winner ── */
  async function confirmWinner() {
    if (!candidate || savingWinner) return;
    setSavingWinner(true);
    const scopeLabel = prizeType === 'campaign'
      ? 'Full Campaign 2026'
      : prizeType === 'matchday'
        ? (matchdayOptions.find(o => o.value === scope)?.label || scope)
        : (weekOptions.find(o => o.value === scope)?.label || scope);
    const prizeLabel = prizeType === 'campaign'
      ? 'Grand Prize'
      : prizeType === 'weekly'
        ? `${scopeLabel} · Winner`
        : `${scopeLabel} · Prize ${nextSlot} of ${ptConfig.slots}`;
    try {
      const r = await fetch('/api/draw-winners', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json', 'x-admin-key': ctx.adminToken },
        body: JSON.stringify({
          prizeType, prizeLabel, scopeKey: scopeK, scopeLabel,
          winnerName: candidate.name, winnerPhone: candidate.phone,
          billNumber: candidate.billNumber, poolSize: eligiblePool.length,
        }),
      });
      if (!r.ok) throw new Error(await r.text());
      const saved = await r.json();
      setFinalWinners(prev => [saved, ...prev]);
      setCandidate(null);
      ctx.setToast({ message: `Winner confirmed: ${candidate.name}`, tone: 'success' });
    } catch (e) {
      ctx.setToast({ message: `Save failed: ${e.message}`, tone: 'error' });
    } finally { setSavingWinner(false); }
  }


  const ptColor = ptConfig.color;

  return (
    <AdminSection title="Lucky Draw" sub="Run official prize draws. Results are saved to the database once confirmed.">

      {/* ── Prize type tabs ── */}
      <div style={{ display: 'flex', gap: 0, marginBottom: 20, borderBottom: '2px solid var(--grey-200)' }}>
        {PRIZE_TYPES.map(pt => (
          <button key={pt.key} onClick={() => setPrizeType(pt.key)} style={{
            padding: '10px 22px', border: 'none', background: 'none', cursor: 'pointer',
            fontFamily: 'inherit', fontSize: 14, fontWeight: 600,
            color: prizeType === pt.key ? pt.color : 'var(--grey-500)',
            borderBottom: `3px solid ${prizeType === pt.key ? pt.color : 'transparent'}`,
            marginBottom: -2, transition: 'color 0.15s',
          }}>
            {pt.label}
            <span style={{ marginLeft: 7, fontSize: 11, fontWeight: 400, color: prizeType === pt.key ? pt.color : 'var(--grey-400)' }}>
              ×{pt.slots}
            </span>
          </button>
        ))}
      </div>
      <div style={{ fontSize: 12.5, color: 'var(--grey-500)', marginBottom: 18, marginTop: -12 }}>{ptConfig.sub}</div>

      {/* ── Scope selector ── */}
      {prizeType !== 'campaign' && (
        <div style={{ marginBottom: 20, maxWidth: 440 }}>
          <FormLabel>{prizeType === 'matchday' ? 'Select Match Day' : 'Select Week'}</FormLabel>
          <select value={scope} onChange={e => { setScope(e.target.value); setCandidate(null); }} style={inputStyle}>
            <option value="">— choose {prizeType === 'matchday' ? 'a match day' : 'a week'} —</option>
            {(prizeType === 'matchday' ? matchdayOptions : weekOptions).map(o => (
              <option key={o.value} value={o.value}>{o.label}</option>
            ))}
          </select>
        </div>
      )}

      {/* ── Main draw area (only when scope is set) ── */}
      {(prizeType === 'campaign' || scope) && (
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 360px', gap: 18, alignItems: 'start' }} className="draw-main-grid">

          {/* Pool panel */}
          <Panel title={`Draw pool · ${pool.length} participant${pool.length !== 1 ? 's' : ''}`} padding={0}>
            <div style={{ padding: '10px 14px', borderBottom: '1px solid var(--grey-100)' }}>
              <input
                value={searchQ}
                onChange={e => setSearchQ(e.target.value)}
                placeholder="Search name, phone or bill..."
                style={{ ...inputStyle, margin: 0 }}
              />
            </div>
            {pool.length === 0 ? (
              <div style={{ padding: 24, textAlign: 'center', color: 'var(--grey-500)', fontSize: 13 }}>
                No participants in this pool yet.
              </div>
            ) : (
              <div style={{ maxHeight: 360, overflowY: 'auto' }}>
                {displayPool.length === 0 ? (
                  <div style={{ padding: 16, color: 'var(--grey-400)', fontSize: 13 }}>No match for "{searchQ}".</div>
                ) : displayPool.map(p => {
                  const isWinner = confirmedPhonesForScope.has(p.phone);
                  return (
                    <div key={p.phone} style={{
                      padding: '9px 14px', borderBottom: '1px solid var(--grey-50)',
                      display: 'flex', alignItems: 'center', gap: 10,
                      background: isWinner ? '#f0fdf4' : candidate?.phone === p.phone ? '#fff7ed' : undefined,
                      opacity: isWinner ? 0.5 : 1,
                    }}>
                      <div style={{
                        width: 32, height: 32, borderRadius: '50%', flexShrink: 0,
                        background: isWinner ? '#16a34a' : ptColor, color: '#fff',
                        display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 13, fontWeight: 700,
                      }}>
                        {isWinner ? '✓' : p.name[0]?.toUpperCase()}
                      </div>
                      <div style={{ flex: 1, minWidth: 0 }}>
                        <div style={{ fontWeight: 600, fontSize: 13, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{p.name}</div>
                        <div style={{ fontSize: 11.5, color: 'var(--grey-500)', fontFamily: "'JetBrains Mono',monospace" }}>{p.phone} · {p.billNumber}</div>
                      </div>
                      <div style={{ fontSize: 11, color: 'var(--grey-400)', textAlign: 'right', flexShrink: 0 }}>
                        {p.count} pred{p.count !== 1 ? 's' : ''}
                        {isWinner && <div style={{ color: '#16a34a', fontWeight: 700 }}>WINNER</div>}
                      </div>
                    </div>
                  );
                })}
              </div>
            )}
          </Panel>

          {/* Draw engine panel */}
          <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>

            {/* Slot indicator */}
            <div style={{ display: 'flex', gap: 8 }}>
              {Array.from({ length: ptConfig.slots }).map((_, i) => {
                const filled = i < slotsFilled;
                const active = i === slotsFilled;
                return (
                  <div key={i} style={{
                    flex: 1, padding: '8px 12px', border: `2px solid ${filled ? '#16a34a' : active ? ptColor : 'var(--grey-200)'}`,
                    background: filled ? '#f0fdf4' : active ? `${ptColor}11` : '#fff',
                    textAlign: 'center', fontSize: 12, fontWeight: 700,
                    color: filled ? '#16a34a' : active ? ptColor : 'var(--grey-400)',
                  }}>
                    {filled ? '✓ Confirmed' : active ? `Prize ${i + 1}` : `Prize ${i + 1}`}
                  </div>
                );
              })}
            </div>

            {/* Winner card / draw prompt */}
            <div style={{
              minHeight: 180, border: `2px dashed ${allSlotsFilled ? '#16a34a' : ptColor}`,
              background: allSlotsFilled ? '#f0fdf4' : '#fff',
              display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center',
              padding: 20, textAlign: 'center', gap: 10,
            }}>
              {allSlotsFilled ? (
                <>
                  <div style={{ fontSize: 28 }}>🏆</div>
                  <div style={{ fontWeight: 700, color: '#16a34a', fontSize: 15 }}>All prizes drawn!</div>
                  <div style={{ fontSize: 12.5, color: 'var(--grey-500)' }}>See winners list below.</div>
                </>
              ) : rolling ? (
                <>
                  <div style={{ fontSize: 32, animation: 'spin 0.4s linear infinite' }}>🎲</div>
                  <div style={{ fontWeight: 700, color: ptColor, fontSize: 14 }}>Drawing...</div>
                  <div style={{ fontSize: 12, color: 'var(--grey-500)' }}>Selecting from {eligiblePool.length} eligible participants</div>
                </>
              ) : candidate ? (
                <>
                  <div style={{ fontSize: 28 }}>🎉</div>
                  <div style={{ fontSize: 11, letterSpacing: '0.15em', textTransform: 'uppercase', color: ptColor, fontWeight: 700, marginBottom: 2 }}>
                    Prize {nextSlot} of {ptConfig.slots}
                  </div>
                  <div style={{ fontWeight: 700, fontSize: 20, lineHeight: 1.2 }}>{candidate.name}</div>
                  <div style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 13, color: 'var(--grey-600)' }}>{candidate.phone}</div>
                  <div style={{ fontFamily: "'JetBrains Mono',monospace", fontSize: 12, color: 'var(--grey-500)' }}>Bill: {candidate.billNumber} · {candidate.outlet}</div>
                  <div style={{ fontSize: 11.5, color: 'var(--grey-400)', marginTop: 2 }}>from pool of {eligiblePool.length}</div>
                </>
              ) : (
                <>
                  <div style={{ fontSize: 28 }}>🎯</div>
                  <div style={{ fontWeight: 600, color: 'var(--grey-500)', fontSize: 14 }}>
                    {pool.length === 0 ? 'No participants yet' : `${eligiblePool.length} eligible · Click to draw`}
                  </div>
                </>
              )}
            </div>

            {/* Action buttons */}
            {!allSlotsFilled && (
              <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
                {!candidate ? (
                  <AdminBtn kind="primary" icon="trophy" disabled={rolling || eligiblePool.length === 0} onClick={runDraw}
                    style={{ background: ptColor, border: 'none' }}>
                    {rolling ? 'Drawing…' : `🎲 Run Draw${ptConfig.slots > 1 ? ` (Prize ${nextSlot} of ${ptConfig.slots})` : ''}`}
                  </AdminBtn>
                ) : (
                  <>
                    <AdminBtn kind="primary" icon="check" disabled={savingWinner} onClick={confirmWinner}
                      style={{ background: '#16a34a', border: 'none' }}>
                      {savingWinner ? 'Saving…' : '✓ Confirm Winner'}
                    </AdminBtn>
                    <AdminBtn kind="ghost" icon="bolt" disabled={savingWinner} onClick={() => setCandidate(null)}>
                      ↺ Re-draw
                    </AdminBtn>
                  </>
                )}
              </div>
            )}
          </div>
        </div>
      )}

      {/* ── Finalized winners list ── */}
      <div style={{ marginTop: 32 }}>
        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 14 }}>
          <div className="font-display" style={{ fontSize: 17 }}>Confirmed Winners</div>
          {finalWinners.length > 0 && (
            <AdminBtn kind="outline" icon="receipt" onClick={() => {
              const rows = finalWinners.map((w, i) => ({
                '#': i + 1, 'Prize Type': w.prize_type, 'Prize': w.prize_label,
                'Scope': w.scope_label, 'Winner': w.winner_name, 'Phone': w.winner_phone,
                'Bill': w.bill_number, 'Pool Size': w.pool_size,
                'Date': new Date(w.finalized_at).toLocaleDateString(),
              }));
              exportRowsToXlsx(`draw_winners_${Date.now()}.xlsx`, 'Draw Winners', rows);
              ctx.setToast({ message: `Exported ${finalWinners.length} winners.`, tone: 'success' });
            }}>Export XLSX</AdminBtn>
          )}
        </div>

        {loadingFinal ? (
          <div style={{ color: 'var(--grey-400)', fontSize: 13 }}>Loading…</div>
        ) : finalWinners.length === 0 ? (
          <div style={{ color: 'var(--grey-400)', fontSize: 13, padding: '20px 0' }}>No confirmed winners yet. Run a draw above to get started.</div>
        ) : (
          <div style={{ overflowX: 'auto' }}>
            <table style={{ width: '100%', borderCollapse: 'collapse', minWidth: 680 }}>
              <thead>
                <tr style={{ background: 'var(--grey-50)' }}>
                  <Th>#</Th><Th>Prize</Th><Th>Scope</Th><Th>Winner</Th><Th>Phone</Th><Th>Bill</Th><Th align="right">Pool</Th><Th>Date</Th>
                </tr>
              </thead>
              <tbody>
                {finalWinners.map((w, i) => {
                  const pt = PRIZE_TYPES.find(t => t.key === w.prize_type);
                  return (
                    <tr key={w.id} style={{ borderTop: '1px solid var(--grey-100)' }}>
                      <Td><span className="font-display" style={{ fontSize: 15, color: pt?.color || 'var(--orange)' }}>{String(i + 1).padStart(2, '0')}</span></Td>
                      <Td>
                        <span style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.1em', textTransform: 'uppercase', color: pt?.color }}>{pt?.label}</span>
                        <div style={{ fontSize: 12, color: 'var(--grey-500)', marginTop: 1 }}>{w.prize_label}</div>
                      </Td>
                      <Td><span style={{ fontSize: 12.5 }}>{w.scope_label}</span></Td>
                      <Td><strong>{w.winner_name}</strong></Td>
                      <Td><span className="font-mono" style={{ fontSize: 12.5 }}>{w.winner_phone}</span></Td>
                      <Td><span className="font-mono" style={{ fontSize: 12.5 }}>{w.bill_number}</span></Td>
                      <Td align="right"><span className="font-mono" style={{ fontSize: 12 }}>{w.pool_size}</span></Td>
                      <Td><span style={{ fontSize: 12, color: 'var(--grey-500)' }}>{new Date(w.finalized_at).toLocaleDateString()}</span></Td>
                    </tr>
                  );
                })}
              </tbody>
            </table>
          </div>
        )}
      </div>

      <style>{`
        @keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
        @media (max-width: 820px) {
          .draw-main-grid { grid-template-columns: 1fr !important; }
        }
      `}</style>
    </AdminSection>
  );
}

/* ============================================================
   FLAG UPLOAD
   ============================================================ */
function AdminFlags({ ctx }) {
  // customFlags: { flagCode: url } from Supabase
  const [customFlags, setCustomFlags] = useState({});
  const [loadingFlags, setLoadingFlags] = useState(true);

  // Upload modal state
  const [activeCode, setActiveCode] = useState(null);  // FIFA flag code being uploaded
  const [activeName, setActiveName] = useState('');
  const [preview,    setPreview]    = useState(null);
  const [busy,       setBusy]       = useState(false);
  const [deleting,   setDeleting]   = useState(null);
  const [msg,        setMsg]        = useState(null);
  const fileRef = React.useRef();

  // Build ordered team list: [{teamKey, flagCode, name}]
  // Uses t.flag as the upload code (not team key) -- fixes SAF->RSA mismatch
  const teams = React.useMemo(() => {
    return Object.entries(TEAMS)
      .map(([k, t]) => ({ teamKey: k, flagCode: t.flag || k, name: t.name }))
      .sort((a, b) => a.name.localeCompare(b.name));
  }, []);

  function loadFlags() {
    setLoadingFlags(true);
    fetch('/api/flags')
      .then(r => r.ok ? r.json() : {})
      .then(data => setCustomFlags(data || {}))
      .catch(() => {})
      .finally(() => setLoadingFlags(false));
  }

  useEffect(() => { loadFlags(); }, []);

  function openUpload(flagCode, name) {
    setActiveCode(flagCode);
    setActiveName(name);
    setPreview(null);
    setMsg(null);
    if (fileRef.current) fileRef.current.value = '';
  }

  function closeUpload() {
    setActiveCode(null);
    setPreview(null);
    setMsg(null);
  }

  function onFile(e) {
    const file = e.target.files[0];
    if (!file) return;
    setMsg(null);
    const reader = new FileReader();
    reader.onload = function(ev) {
      const img = new Image();
      img.onload = function() {
        const canvas = document.createElement('canvas');
        canvas.width = 320; canvas.height = 213;
        const c = canvas.getContext('2d');
        const srcRatio = img.width / img.height;
        const dstRatio = 320 / 213;
        let sx = 0, sy = 0, sw = img.width, sh = img.height;
        if (srcRatio > dstRatio) { sw = img.height * dstRatio; sx = (img.width - sw) / 2; }
        else { sh = img.width / dstRatio; sy = (img.height - sh) / 2; }
        c.drawImage(img, sx, sy, sw, sh, 0, 0, 320, 213);
        setPreview(canvas.toDataURL('image/png'));
      };
      img.src = ev.target.result;
    };
    reader.readAsDataURL(file);
  }

  async function upload() {
    if (!preview || !activeCode) return;
    setBusy(true); setMsg(null);
    try {
      const res = await fetch('/api/flags', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json', 'x-admin-key': ctx.adminToken },
        body: JSON.stringify({ code: activeCode, imageData: preview }),
      });
      const data = await res.json();
      if (!res.ok) {
        setMsg({ ok: false, text: data.error || 'Upload failed.' });
      } else {
        setMsg({ ok: true, text: activeCode + ' uploaded.' });
        loadFlags();
        setTimeout(closeUpload, 1200);
      }
    } catch(_) {
      setMsg({ ok: false, text: 'Network error.' });
    }
    setBusy(false);
  }

  async function deleteFlag(flagCode) {
    if (!window.confirm('Remove custom flag for ' + flagCode + '? Reverts to CDN default.')) return;
    setDeleting(flagCode);
    try {
      const res = await fetch('/api/flags?code=' + encodeURIComponent(flagCode), {
        method: 'DELETE',
        headers: { 'x-admin-key': ctx.adminToken },
      });
      if (res.ok) {
        loadFlags();
        ctx.setToast && ctx.setToast({ message: flagCode + ' custom flag removed.', tone: 'success' });
      } else {
        const d = await res.json().catch(() => ({}));
        ctx.setToast && ctx.setToast({ message: d.error || 'Delete failed.', tone: 'error' });
      }
    } catch(_) {
      ctx.setToast && ctx.setToast({ message: 'Network error.', tone: 'error' });
    }
    setDeleting(null);
  }

  const customCount = Object.keys(customFlags).length;

  return (
    <AdminSection
      title="Flag Images"
      sub={'Custom uploads override the CDN flag for any team. ' + customCount + ' custom flag' + (customCount !== 1 ? 's' : '') + ' active.'}
    >
      {/* Upload modal overlay */}
      {activeCode && (
        <div style={{
          position: 'fixed', inset: 0, zIndex: 60,
          background: 'rgba(17,6,24,0.55)',
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          padding: 20,
        }} onClick={closeUpload}>
          <div style={{
            background: '#fff', borderRadius: 12, padding: 28, width: '100%', maxWidth: 480,
            boxShadow: '0 24px 64px rgba(17,6,24,0.30)',
          }} onClick={e => e.stopPropagation()}>
            <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 20 }}>
              <div>
                <div className="font-display" style={{ fontSize: 20, letterSpacing: '-0.01em' }}>
                  Upload flag for {activeName}
                </div>
                <div style={{ fontSize: 12, color: 'var(--grey-500)', marginTop: 3 }}>
                  Code: {activeCode} -- auto-resized to 320x213 PNG (3:2)
                </div>
              </div>
              <button onClick={closeUpload} style={{ background: 'none', border: '1.5px solid var(--grey-200)', borderRadius: 6, padding: '6px 10px', cursor: 'pointer', fontSize: 16, color: 'var(--grey-500)' }}>x</button>
            </div>

            {/* Current flag side-by-side */}
            <div style={{ display: 'flex', gap: 20, marginBottom: 20 }}>
              <div style={{ flex: 1 }}>
                <div style={{ fontSize: 10, letterSpacing: '0.14em', textTransform: 'uppercase', fontWeight: 700, color: 'var(--grey-500)', marginBottom: 6 }}>Current</div>
                {customFlags[activeCode] ? (
                  <img src={customFlags[activeCode] + '?v=' + Date.now()} style={{ width: '100%', height: 80, objectFit: 'cover', borderRadius: 6, border: '2px solid var(--orange)' }} alt="current" />
                ) : (
                  <img src={'https://flagcdn.com/' + (window._FIFA_ISO_ADMIN && window._FIFA_ISO_ADMIN[activeCode] ? window._FIFA_ISO_ADMIN[activeCode] : activeCode.toLowerCase()) + '.svg'} style={{ width: '100%', height: 80, objectFit: 'cover', borderRadius: 6, border: '1.5px solid var(--grey-200)' }} alt="cdn" />
                )}
                <div style={{ fontSize: 10, color: customFlags[activeCode] ? 'var(--orange)' : 'var(--grey-400)', textAlign: 'center', marginTop: 4, fontWeight: 600 }}>
                  {customFlags[activeCode] ? 'CUSTOM' : 'CDN DEFAULT'}
                </div>
              </div>
              {preview && (
                <div style={{ flex: 1 }}>
                  <div style={{ fontSize: 10, letterSpacing: '0.14em', textTransform: 'uppercase', fontWeight: 700, color: 'var(--grey-500)', marginBottom: 6 }}>New upload</div>
                  <img src={preview} style={{ width: '100%', height: 80, objectFit: 'cover', borderRadius: 6, border: '2px solid var(--success)' }} alt="new" />
                  <div style={{ fontSize: 10, color: 'var(--success)', textAlign: 'center', marginTop: 4, fontWeight: 600 }}>READY TO UPLOAD</div>
                </div>
              )}
            </div>

            {/* File picker */}
            <input
              ref={fileRef}
              type="file"
              accept="image/*"
              onChange={onFile}
              style={{
                display: 'block', width: '100%', padding: '10px 12px',
                border: '1.5px solid var(--grey-200)', borderRadius: 6,
                fontSize: 13, cursor: 'pointer', background: 'var(--grey-50)',
                marginBottom: 14,
              }}
            />

            {msg && (
              <div style={{
                padding: '10px 14px', borderRadius: 6, fontSize: 13, fontWeight: 500, marginBottom: 14,
                background: msg.ok ? '#f0fdf4' : '#fef2f2',
                color: msg.ok ? 'var(--success)' : '#dc2626',
                border: '1px solid ' + (msg.ok ? '#bbf7d0' : '#fecaca'),
              }}>{msg.text}</div>
            )}

            <div style={{ display: 'flex', gap: 10 }}>
              <AdminBtn kind="primary" icon="upload" onClick={upload} disabled={busy || !preview}>
                {busy ? 'Uploading...' : 'Upload'}
              </AdminBtn>
              <AdminBtn kind="outline" onClick={closeUpload}>Cancel</AdminBtn>
            </div>
          </div>
        </div>
      )}

      {/* All teams grid */}
      <Panel title={'All 48 teams -- click any flag to upload a replacement' + (loadingFlags ? ' (loading...)' : '')}>
        <div style={{
          display: 'grid',
          gridTemplateColumns: 'repeat(auto-fill, minmax(130px, 1fr))',
          gap: 12,
        }}>
          {teams.map(({ teamKey, flagCode, name }) => {
            const hasCustom = !!customFlags[flagCode];
            const iso = window._FIFA_ISO_ADMIN && window._FIFA_ISO_ADMIN[flagCode] ? window._FIFA_ISO_ADMIN[flagCode] : flagCode.toLowerCase();
            const displaySrc = hasCustom
              ? customFlags[flagCode] + '?v=' + Date.now()
              : 'https://flagcdn.com/' + iso + '.svg';
            return (
              <div key={teamKey} style={{ position: 'relative' }}>
                <button
                  onClick={() => openUpload(flagCode, name)}
                  style={{
                    width: '100%', background: 'none', border: '2px solid ' + (hasCustom ? 'var(--orange)' : 'var(--grey-200)'),
                    borderRadius: 8, padding: '10px 8px 8px',
                    cursor: 'pointer', display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 6,
                    transition: 'border-color 0.15s',
                  }}
                  onMouseEnter={e => { e.currentTarget.style.borderColor = 'var(--orange)'; }}
                  onMouseLeave={e => { e.currentTarget.style.borderColor = hasCustom ? 'var(--orange)' : 'var(--grey-200)'; }}
                >
                  <img
                    src={displaySrc}
                    style={{ width: 72, height: 48, objectFit: 'cover', borderRadius: 4, border: '1px solid var(--grey-100)' }}
                    alt={name}
                    onError={e => { e.currentTarget.style.opacity = '0.25'; }}
                  />
                  <div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.04em', textAlign: 'center', lineHeight: 1.2 }}>{name}</div>
                  <div style={{ fontSize: 9, letterSpacing: '0.12em', textTransform: 'uppercase', color: hasCustom ? 'var(--orange)' : 'var(--grey-400)', fontWeight: 700 }}>
                    {hasCustom ? 'Custom' : flagCode}
                  </div>
                </button>
                {/* Delete custom flag */}
                {hasCustom && (
                  <button
                    onClick={e => { e.stopPropagation(); deleteFlag(flagCode); }}
                    disabled={deleting === flagCode}
                    title="Remove custom flag"
                    style={{
                      position: 'absolute', top: 4, right: 4,
                      width: 20, height: 20, borderRadius: '50%',
                      background: '#fee2e2', border: '1px solid #fca5a5',
                      color: '#b91c1c', fontSize: 12, fontWeight: 700,
                      cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center',
                      lineHeight: 1, padding: 0,
                    }}
                  >x</button>
                )}
              </div>
            );
          })}
        </div>
      </Panel>
    </AdminSection>
  );
}

/* ─── Audit Log ─────────────────────────────────────────────────────────── */
function AdminAuditLog({ ctx }) {
  const { adminToken } = ctx;

  const [logs,      setLogs]      = React.useState([]);
  const [loading,   setLoading]   = React.useState(false);
  const [error,     setError]     = React.useState('');
  const [actorType, setActorType] = React.useState('all');
  const [dateFrom,  setDateFrom]  = React.useState('');
  const [dateTo,    setDateTo]    = React.useState('');

  const fetchLogs = React.useCallback(() => {
    setLoading(true);
    const params = new URLSearchParams({ limit: '300' });
    if (actorType !== 'all') params.set('actor_type', actorType);
    if (dateFrom) params.set('date_from', dateFrom + 'T00:00:00Z');
    if (dateTo)   params.set('date_to',   dateTo   + 'T23:59:59Z');

    fetch(`/api/audit-log?${params}`, { headers: { 'x-admin-key': adminToken } })
      .then(r => r.json())
      .then(data => { setLogs(Array.isArray(data) ? data : []); setError(''); })
      .catch(() => setError('Failed to load audit log.'))
      .finally(() => setLoading(false));
  }, [adminToken, actorType, dateFrom, dateTo]);

  React.useEffect(() => { fetchLogs(); }, [fetchLogs]);

  React.useEffect(() => {
    const id = setInterval(fetchLogs, 30000);
    return () => clearInterval(id);
  }, [fetchLogs]);

  const TYPE_COLOR = { user: '#2563eb', admin: 'var(--orange)', system: '#6b7280' };
  const TYPE_LABEL = { user: 'User', admin: 'Admin', system: 'System' };

  const selStyle = {
    padding: '8px 10px', border: '1px solid #3f3f46', borderRadius: 7,
    background: '#18181b', color: '#f4f4f5', fontSize: 13,
  };

  return (
    <AdminSection title="Audit Log" sub="All system activity — read only, newest first.">
      <Panel>
        {/* Filters */}
        <div style={{ display: 'flex', gap: 12, flexWrap: 'wrap', marginBottom: 18, alignItems: 'flex-end' }}>
          <div>
            <FormLabel>Actor type</FormLabel>
            <select value={actorType} onChange={e => setActorType(e.target.value)} style={selStyle}>
              <option value="all">All types</option>
              <option value="user">User (public)</option>
              <option value="admin">Admin</option>
              <option value="system">System / Webhook</option>
            </select>
          </div>
          <div>
            <FormLabel>From</FormLabel>
            <input type="date" value={dateFrom} onChange={e => setDateFrom(e.target.value)} style={selStyle} />
          </div>
          <div>
            <FormLabel>To</FormLabel>
            <input type="date" value={dateTo} onChange={e => setDateTo(e.target.value)} style={selStyle} />
          </div>
          <AdminBtn onClick={fetchLogs}>Refresh</AdminBtn>
          {(dateFrom || dateTo || actorType !== 'all') && (
            <AdminBtn kind="ghost" onClick={() => { setActorType('all'); setDateFrom(''); setDateTo(''); }}>
              Clear filters
            </AdminBtn>
          )}
        </div>

        {error && <p style={{ color: '#f87171', marginBottom: 12, fontSize: 13 }}>{error}</p>}
        {loading && logs.length === 0 && <p style={{ color: '#a1a1aa', fontSize: 13 }}>Loading…</p>}

        <div style={{ overflowX: 'auto' }}>
          <table style={{ width: '100%', borderCollapse: 'collapse', fontSize: 13 }}>
            <thead>
              <tr>
                <Th>Time (MVT)</Th>
                <Th>Type</Th>
                <Th>Actor</Th>
                <Th>Action</Th>
                <Th>Entity</Th>
                <Th>Detail</Th>
              </tr>
            </thead>
            <tbody>
              {logs.length === 0 && !loading && (
                <tr>
                  <td colSpan={6} style={{ textAlign: 'center', color: '#52525b', padding: '28px 0', fontSize: 13 }}>
                    No log entries found.
                  </td>
                </tr>
              )}
              {logs.map(log => {
                const col = TYPE_COLOR[log.actor_type] || '#6b7280';
                return (
                  <tr key={log.id} style={{ borderBottom: '1px solid #27272a' }}>
                    <Td style={{ whiteSpace: 'nowrap', color: '#a1a1aa', fontSize: 11 }}>
                      {new Date(log.created_at).toLocaleString('en-MV', {
                        timeZone: 'Indian/Maldives', hour12: false,
                        year: 'numeric', month: '2-digit', day: '2-digit',
                        hour: '2-digit', minute: '2-digit', second: '2-digit',
                      })}
                    </Td>
                    <Td>
                      <span style={{
                        background: col + '22', color: col,
                        border: `1px solid ${col}55`,
                        borderRadius: 4, padding: '2px 8px',
                        fontSize: 11, fontWeight: 700, whiteSpace: 'nowrap',
                      }}>
                        {TYPE_LABEL[log.actor_type] || log.actor_type}
                      </span>
                    </Td>
                    <Td style={{ maxWidth: 150, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
                      {log.actor_name
                        ? <><span style={{ color: '#f4f4f5' }}>{log.actor_name}</span>
                            {log.actor_id && log.actor_id !== log.actor_name
                              ? <span style={{ color: '#71717a', marginLeft: 4, fontSize: 11 }}>{log.actor_id}</span>
                              : null}
                          </>
                        : <span style={{ color: '#71717a' }}>{log.actor_id || '—'}</span>
                      }
                    </Td>
                    <Td>
                      <span style={{ fontFamily: 'monospace', color: '#fbbf24', fontWeight: 600, fontSize: 12 }}>
                        {log.action}
                      </span>
                    </Td>
                    <Td style={{ color: '#a1a1aa', fontSize: 11, whiteSpace: 'nowrap' }}>
                      {[log.entity_type, log.entity_id].filter(Boolean).join(' · ') || '—'}
                    </Td>
                    <Td style={{ maxWidth: 260, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', color: '#52525b', fontSize: 11 }}
                      title={log.detail ? JSON.stringify(log.detail, null, 2) : ''}>
                      {log.detail ? JSON.stringify(log.detail) : '—'}
                    </Td>
                  </tr>
                );
              })}
            </tbody>
          </table>
        </div>

        {logs.length > 0 && (
          <p style={{ color: '#52525b', fontSize: 12, marginTop: 10, textAlign: 'right' }}>
            {logs.length} entries · auto-refreshes every 30 s
          </p>
        )}
      </Panel>
    </AdminSection>
  );
}

Object.assign(window, {
  AdminOverview, AdminMatches, AdminCreate, AdminSettle, AdminDraw, AdminFlags,
  AdminAuditLog,
  Th, Td, IconBtn, FormLabel, FormSelect, StatCard,
});
