import type { Metadata } from 'next';
import Link from 'next/link';
import { addDaysToDate, businessDate, forVenue, getDb } from '@pintrush/db';
import { formatBusinessDate, formatNumber, formatPercent } from '@/components/host/format';
import { Notice, PageHeader, Panel, StatCard, TableWrap, td, th } from '@/components/host/ui';
import { cn } from '@/lib/cn';
import { requireHost } from '@/lib/auth/session';
import { formatSeconds, PILOT_TARGETS, share, targetStatus, type TargetStatus } from '@/lib/analytics';
import { CheckIcon, InfoIcon } from '@/components/ui/icons';
import { dailyJoins, pilotMetrics, type PilotMetrics } from '../../_lib/data';

export const metadata: Metadata = { title: 'Metrics' };

const RANGES = [7, 30, 90] as const;
type Range = (typeof RANGES)[number];
const DAY_MS = 86_400_000;
/** Spec §11 pilot hypothesis. */
const RETURN_TARGET = PILOT_TARGETS.returningGuests;

function parseRange(value: string | string[] | undefined): Range {
  const n = Number(Array.isArray(value) ? value[0] : value);
  return (RANGES as readonly number[]).includes(n) ? (n as Range) : 30;
}

export default async function MetricsPage({ searchParams }: { searchParams: Promise<Record<string, string | string[] | undefined>> }) {
  const { venue } = await requireHost('/host/metrics');
  const range = parseRange((await searchParams).range);
  const now = Date.now();
  const from = new Date(now - range * DAY_MS);
  const to = new Date(now);
  const [metrics, daily, pilot] = await Promise.all([
    forVenue(getDb(), venue.id).metrics({ from, to }),
    dailyJoins(venue.id, venue.timezone, from, to),
    pilotMetrics(venue.id, venue.timezone, from, to),
  ]);

  // One bar per business date in the range, including nights nobody played.
  const byDate = new Map(daily.map((d) => [d.date, d]));
  const lastDate = businessDate(venue.timezone, now);
  const dates = Array.from({ length: range }, (_, i) => addDaysToDate(lastDate, i - range + 1));
  const series = dates.map((date) => byDate.get(date) ?? { date, joins: 0, guests: 0 });
  const realPeak = Math.max(0, ...series.map((d) => d.joins));
  const peak = Math.max(1, realPeak);
  const repeatRate = metrics.uniqueGuests > 0 ? metrics.repeatPlayers / metrics.uniqueGuests : 0;
  const nightsWithPlay = series.filter((d) => d.joins > 0).length;
  const totalEvents = metrics.eventsByKind.ALWAYS_ON + metrics.eventsByKind.CHAMPIONSHIP;

  return (
    <div className="flex flex-col gap-8">
      <PageHeader
        title="Metrics"
        description="Guests are counted by an anonymous device id — no names, emails or phone numbers are collected."
        actions={
          <nav aria-label="Date range" className="inline-flex rounded-xl border border-line bg-surface p-1">
            {RANGES.map((r) => (
              <Link
                key={r}
                href={`/host/metrics?range=${r}`}
                aria-current={r === range ? 'page' : undefined}
                className={cn(
                  'inline-flex min-h-11 items-center rounded-lg px-4 text-sm font-semibold',
                  r === range ? 'bg-violet/25 text-fg shadow-[inset_0_0_0_1px_rgb(124_92_255/0.55)]' : 'text-muted hover:text-fg',
                )}
              >
                {r} days
              </Link>
            ))}
          </nav>
        }
      />

      <dl className="grid grid-cols-2 gap-3 lg:grid-cols-4">
        <StatCard label="Unique guests" value={formatNumber(metrics.uniqueGuests)} hint={`last ${range} days`} tone="violet" />
        <StatCard label="Joins" value={formatNumber(metrics.joins)} hint={metrics.uniqueGuests ? `${(metrics.joins / metrics.uniqueGuests).toFixed(1)} per guest` : undefined} tone="cyan" />
        <StatCard label="Rounds completed" value={formatNumber(metrics.roundsCompleted)} hint={`${nightsWithPlay} night${nightsWithPlay === 1 ? '' : 's'} with play`} tone="lime" />
        <StatCard label="Repeat players" value={formatNumber(metrics.repeatPlayers)} hint="played on 2+ different nights" tone="amber" />
      </dl>

      <div className="grid gap-8 lg:grid-cols-[1.6fr_1fr]">
        <Panel title="Joins per night" description="Business nights run until 5 am, so a 1 am join counts for the night before.">
          <div className="flex h-48 items-end gap-[2px]" aria-hidden="true">
            {series.map((d) => (
              <div key={d.date} className="group relative flex h-full flex-1 items-end" title={`${formatBusinessDate(d.date)}: ${d.joins} joins, ${d.guests} guests`}>
                <div
                  className={cn('w-full rounded-t-sm', d.joins > 0 ? 'bg-[linear-gradient(180deg,var(--color-cyan),var(--color-violet))]' : 'bg-line/60')}
                  style={{ height: `${d.joins > 0 ? Math.max(4, (d.joins / peak) * 100) : 2}%` }}
                />
              </div>
            ))}
          </div>
          <div className="mt-2 flex justify-between text-xs text-subtle" aria-hidden="true">
            <span>{formatBusinessDate(series[0]!.date)}</span>
            <span>peak {formatNumber(realPeak)}</span>
            <span>{formatBusinessDate(series[series.length - 1]!.date)}</span>
          </div>
          <details className="mt-4 text-sm">
            <summary className="inline-flex min-h-11 items-center font-semibold text-violet-soft hover:text-fg">Show as a table</summary>
            <div className="mt-2 px-5 sm:px-6">
              <TableWrap label="Joins per night">
                <thead>
                  <tr>
                    <th scope="col" className={th}>
                      Night
                    </th>
                    <th scope="col" className={`${th} text-right`}>
                      Joins
                    </th>
                    <th scope="col" className={`${th} text-right`}>
                      Guests
                    </th>
                  </tr>
                </thead>
                <tbody>
                  {[...series].reverse().map((d) => (
                    <tr key={d.date}>
                      <td className={td}>{formatBusinessDate(d.date)}</td>
                      <td className={`${td} num text-right`}>{d.joins}</td>
                      <td className={`${td} num text-right`}>{d.guests}</td>
                    </tr>
                  ))}
                </tbody>
              </TableWrap>
            </div>
          </details>
        </Panel>

        <div className="flex flex-col gap-8">
          <Panel title="Coming back">
            <p className="num font-display text-4xl font-bold">{metrics.uniqueGuests ? formatPercent(repeatRate) : '—'}</p>
            <p className="mt-1 text-sm text-muted">of guests played on more than one night in the last {range} days.</p>
            <div
              className="relative mt-5 h-3 rounded-full bg-line/70"
              role="meter"
              aria-label="Share of guests who came back"
              aria-valuemin={0}
              aria-valuemax={100}
              aria-valuenow={Math.round(repeatRate * 100)}
              aria-valuetext={`${formatPercent(repeatRate)}, target 30%`}
            >
              <div
                className={cn('h-full rounded-full', repeatRate >= RETURN_TARGET ? 'bg-lime' : 'bg-amber')}
                style={{ width: `${Math.min(100, repeatRate * 100)}%` }}
              />
              <div className="absolute inset-y-[-4px] w-0.5 bg-fg" style={{ left: `${RETURN_TARGET * 100}%` }} aria-hidden="true" />
            </div>
            <p className="mt-2 text-xs text-subtle">
              Pilot target: <strong className="text-fg">≥ 30%</strong> of identifiable players returning within 30 days (a hypothesis, not a benchmark).
            </p>
          </Panel>
          <Panel title="Events started">
            <ul className="flex flex-col gap-3">
              {(
                [
                  ['Always-on nights', metrics.eventsByKind.ALWAYS_ON, 'bg-cyan'],
                  ['Championships', metrics.eventsByKind.CHAMPIONSHIP, 'bg-violet'],
                ] as const
              ).map(([label, count, color]) => (
                <li key={label}>
                  <div className="flex justify-between text-sm">
                    <span className="text-muted">{label}</span>
                    <span className="num font-semibold">{formatNumber(count)}</span>
                  </div>
                  <div className="mt-1.5 h-2 rounded-full bg-line/70" aria-hidden="true">
                    <div className={cn('h-full rounded-full', color)} style={{ width: `${totalEvents ? (count / totalEvents) * 100 : 0}%` }} />
                  </div>
                </li>
              ))}
            </ul>
          </Panel>
        </div>
      </div>
      <PilotScorecard pilot={pilot} range={range} />

      {metrics.joins === 0 && (
        <Notice tone="info" title="No play recorded in this range yet">
          Numbers fill in as soon as guests join from the QR code on your TV.
        </Notice>
      )}
    </div>
  );
}

/** The pilot hypotheses and tracked signals from spec §11, each next to its target. */
function PilotScorecard({ pilot, range }: { pilot: PilotMetrics; range: Range }) {
  const multiRound = share(pilot.visits.multiRound, pilot.visits.played);
  const reconnectsPerPlayer = share(pilot.reconnects.total, pilot.reconnects.players);
  const { ALWAYS_ON: alwaysOn, CHAMPIONSHIP: shows } = pilot.hostEffort;
  const totalEvents = alwaysOn.events + shows.events;
  const actionsPerEvent = share(alwaysOn.actions + shows.actions, totalEvents);
  const fairShare = share(pilot.fairness.yes, pilot.fairness.votes);
  const perEvent = (x: { events: number; actions: number }) => (x.events ? (x.actions / x.events).toFixed(1) : '—');

  return (
    <Panel
      title="Pilot scorecard"
      description={`Spec §11 pilot hypotheses for the last ${range} days — targets to judge the pilot by, not benchmarks. Signals without a target are tracked so the pilot can set one.`}
    >
      <dl className="grid gap-3 sm:grid-cols-2 xl:grid-cols-3">
        <PilotTile
          label="Visits with 2+ rounds"
          value={multiRound === null ? '—' : formatPercent(multiRound)}
          detail={
            pilot.visits.played
              ? `${formatNumber(pilot.visits.multiRound)} of ${formatNumber(pilot.visits.played)} visits (one phone, one night) answered in 2+ rounds`
              : 'No answered rounds in this range yet'
          }
          target={{ label: '≥ 50% play more than one round per visit', status: targetStatus(multiRound, PILOT_TARGETS.multiRoundVisits) }}
          meter={multiRound === null ? undefined : { value: multiRound, target: PILOT_TARGETS.multiRoundVisits }}
        />
        <PilotTile
          label="Answer time (median)"
          value={formatSeconds(pilot.latency.medianMs)}
          detail={
            pilot.latency.answers
              ? `90% of answers within ${formatSeconds(pilot.latency.p90Ms)} · ${formatNumber(pilot.latency.answers)} answers, timed by the server from question open`
              : 'No answers in this range yet'
          }
        />
        <PilotTile
          label="Reconnects per player"
          value={reconnectsPerPlayer === null ? '—' : reconnectsPerPlayer.toFixed(2)}
          detail={
            pilot.reconnects.players
              ? `${formatNumber(pilot.reconnects.reconnected)} of ${formatNumber(pilot.reconnects.players)} players lost the connection at least once after reaching the game screen`
              : 'No players in this range yet'
          }
        />
        <PilotTile
          label="Host actions per event"
          value={actionsPerEvent === null ? '—' : actionsPerEvent.toFixed(1)}
          detail={
            totalEvents
              ? `Pauses, skips, kicks, renames… ${perEvent(shows)} per championship (${formatNumber(shows.events)}) · ${perEvent(alwaysOn)} per always-on night (${formatNumber(alwaysOn.events)})`
              : 'No events started in this range yet'
          }
        />
        <PilotTile
          label="“Was that round fair?”"
          value={fairShare === null ? '—' : formatPercent(fairShare)}
          detail={pilot.fairness.votes ? `said yes · ${formatNumber(pilot.fairness.votes)} one-tap votes from phones after a round` : 'No votes yet — phones ask after a round'}
        />
      </dl>
    </Panel>
  );
}

const TARGET_COPY: Record<TargetStatus, { label: string; className: string }> = {
  met: { label: 'Target met', className: 'border-lime/40 bg-lime/10 text-lime-soft' },
  below: { label: 'Below target', className: 'border-amber/45 bg-amber/10 text-amber-soft' },
  'no-data': { label: 'No data yet', className: 'border-line bg-night/40 text-muted' },
};

function PilotTile({
  label,
  value,
  detail,
  target,
  meter,
}: {
  label: string;
  value: string;
  detail: string;
  /** A spec target; tiles without one say they are tracked only. */
  target?: { label: string; status: TargetStatus };
  meter?: { value: number; target: number };
}) {
  const copy = target ? TARGET_COPY[target.status] : null;
  return (
    <div className="flex flex-col gap-2 rounded-2xl border border-line bg-night/40 p-4 sm:p-5">
      <dt className="text-sm font-medium text-muted">{label}</dt>
      <dd className="num font-display text-3xl font-bold text-fg">{value}</dd>
      {meter && (
        <dd
          className="relative mt-1 h-2.5 rounded-full bg-line/70"
          role="meter"
          aria-label={label}
          aria-valuemin={0}
          aria-valuemax={100}
          aria-valuenow={Math.round(meter.value * 100)}
          aria-valuetext={`${formatPercent(meter.value)}, target ${formatPercent(meter.target)}`}
        >
          <div className={cn('h-full rounded-full', meter.value >= meter.target ? 'bg-lime' : 'bg-amber')} style={{ width: `${Math.min(100, meter.value * 100)}%` }} />
          <div className="absolute inset-y-[-4px] w-0.5 bg-fg" style={{ left: `${meter.target * 100}%` }} aria-hidden="true" />
        </dd>
      )}
      <dd className="text-sm text-muted">{detail}</dd>
      <dd className="mt-auto flex flex-wrap items-center gap-2 pt-1 text-xs">
        {target && copy ? (
          <>
            <span className={cn('inline-flex items-center gap-1 rounded-full border px-2 py-0.5 font-semibold', copy.className)}>
              {target.status === 'met' ? <CheckIcon className="size-3.5" /> : <InfoIcon className="size-3.5" />}
              {copy.label}
            </span>
            <span className="text-subtle">Pilot target: {target.label}</span>
          </>
        ) : (
          <span className="text-subtle">Spec: tracked during the pilot (no target set)</span>
        )}
      </dd>
    </div>
  );
}
