/**
 * Admin "Site analytics" panel: what the cookie-less page-view beacon (/api/a) has recorded.
 * Server component — reads AnalyticsEvent (kind PAGE_VIEW; path + referrer host only, never a
 * person) and compares /pilot views with the pilot applications received in the same days.
 */
import { businessDate, getDb } from '@pintrush/db';
import { formatBusinessDate, formatNumber, formatPercent } from '@/components/host/format';
import { Panel, TableWrap, td, th } from '@/components/host/ui';
import { fillDailySeries, share } from '@/lib/analytics';
import { cn } from '@/lib/cn';

const DAYS = 30;
const DAY_MS = 86_400_000;
const TOP = 8;

interface SiteAnalyticsData {
  series: { day: string; count: number }[];
  total: number;
  topPaths: { label: string; count: number }[];
  topReferrers: { label: string; count: number }[];
  pilotViews: number;
  pilotLeads: number;
}

/** Last 30 calendar days in `timeZone`, today included. */
async function loadSiteAnalytics(timeZone: string, now: number): Promise<SiteAnalyticsData> {
  const db = getDb();
  const today = businessDate(timeZone, now, 0);
  const firstDay = fillDailySeries([], today, DAYS)[0]?.day ?? today;
  // Every query uses the same window: whole local days from firstDay. The plain `createdAt` bound
  // (a day wider) lets Postgres use the (kind, createdAt) index; the local-date test is exact.
  // Timestamps are UTC `timestamp`s, converted explicitly like the host metrics.
  const since = new Date(now - (DAYS + 1) * DAY_MS).toISOString();
  const [daily, paths, referrers, leads] = await Promise.all([
    db.$queryRaw<{ d: Date; views: number }[]>`
      SELECT (("createdAt" AT TIME ZONE 'UTC') AT TIME ZONE ${timeZone}::text)::date AS d, COUNT(*)::int AS views
      FROM "AnalyticsEvent"
      WHERE "kind" = 'PAGE_VIEW'
        AND "createdAt" >= (${since}::timestamptz AT TIME ZONE 'UTC')
        AND (("createdAt" AT TIME ZONE 'UTC') AT TIME ZONE ${timeZone}::text)::date >= ${firstDay}::date
      GROUP BY d`,
    db.$queryRaw<{ label: string; views: number }[]>`
      SELECT "path" AS label, COUNT(*)::int AS views
      FROM "AnalyticsEvent"
      WHERE "kind" = 'PAGE_VIEW' AND "path" IS NOT NULL
        AND "createdAt" >= (${since}::timestamptz AT TIME ZONE 'UTC')
        AND (("createdAt" AT TIME ZONE 'UTC') AT TIME ZONE ${timeZone}::text)::date >= ${firstDay}::date
      GROUP BY "path" ORDER BY views DESC, "path" LIMIT 50`,
    db.$queryRaw<{ label: string; views: number }[]>`
      SELECT "referrer" AS label, COUNT(*)::int AS views
      FROM "AnalyticsEvent"
      WHERE "kind" = 'PAGE_VIEW' AND "referrer" IS NOT NULL
        AND "createdAt" >= (${since}::timestamptz AT TIME ZONE 'UTC')
        AND (("createdAt" AT TIME ZONE 'UTC') AT TIME ZONE ${timeZone}::text)::date >= ${firstDay}::date
      GROUP BY "referrer" ORDER BY views DESC, "referrer" LIMIT ${TOP}`,
    db.$queryRaw<{ n: number }[]>`
      SELECT COUNT(*)::int AS n
      FROM "PilotLead"
      WHERE "createdAt" >= (${since}::timestamptz AT TIME ZONE 'UTC')
        AND (("createdAt" AT TIME ZONE 'UTC') AT TIME ZONE ${timeZone}::text)::date >= ${firstDay}::date`,
  ]);
  const series = fillDailySeries(
    daily.map((r) => ({ day: r.d.toISOString().slice(0, 10), count: Number(r.views) })),
    today,
    DAYS,
  );
  const pathRows = paths.map((p) => ({ label: p.label, count: Number(p.views) }));
  return {
    series,
    total: series.reduce((n, d) => n + d.count, 0),
    topPaths: pathRows.slice(0, TOP),
    topReferrers: referrers.map((r) => ({ label: r.label, count: Number(r.views) })),
    pilotViews: pathRows.find((p) => p.label === '/pilot')?.count ?? 0,
    pilotLeads: Number(leads[0]?.n ?? 0),
  };
}

export async function SiteAnalytics({ timeZone, now = Date.now() }: { timeZone: string; now?: number }) {
  const data = await loadSiteAnalytics(timeZone, now);
  const peak = Math.max(1, ...data.series.map((d) => d.count));
  const conversion = share(data.pilotLeads, data.pilotViews);
  const first = data.series[0];
  const last = data.series[data.series.length - 1];

  return (
    <Panel
      title="Site analytics"
      description={`Marketing page views in the last ${DAYS} days, from the cookie-less beacon (no cookies, no IPs; bots and Do Not Track visitors are never counted).`}
    >
      <div className="flex flex-col gap-8">
        <dl className="grid grid-cols-2 gap-3 md:grid-cols-4">
          <Figure label="Page views" value={formatNumber(data.total)} hint={`last ${DAYS} days`} />
          <Figure label="Per day" value={formatNumber(Math.round(data.total / DAYS))} hint="average" />
          <Figure label="/pilot views" value={formatNumber(data.pilotViews)} hint="the application page" />
          <Figure
            label="Pilot applications"
            value={formatNumber(data.pilotLeads)}
            hint={conversion === null ? 'no /pilot views yet' : `${formatPercent(conversion)} of /pilot views`}
          />
        </dl>

        <div>
          <h3 className="mb-3 text-sm font-semibold text-muted">Page views per day</h3>
          <div className="flex h-40 items-end gap-[2px]" aria-hidden="true">
            {data.series.map((d) => (
              <div key={d.day} className="relative flex h-full flex-1 items-end" title={`${formatBusinessDate(d.day)}: ${formatNumber(d.count)} views`}>
                <div
                  className={cn('w-full rounded-t-sm', d.count > 0 ? 'bg-[linear-gradient(180deg,var(--color-cyan),var(--color-violet))]' : 'bg-line/60')}
                  style={{ height: `${d.count > 0 ? Math.max(4, (d.count / peak) * 100) : 2}%` }}
                />
              </div>
            ))}
          </div>
          <div className="mt-2 flex justify-between text-xs text-subtle" aria-hidden="true">
            <span>{first ? formatBusinessDate(first.day) : ''}</span>
            <span>peak {formatNumber(Math.max(0, ...data.series.map((d) => d.count)))}</span>
            <span>{last ? formatBusinessDate(last.day) : ''}</span>
          </div>
          <details className="mt-3 text-sm">
            <summary className="inline-flex min-h-11 items-center font-semibold text-violet-soft hover:text-fg">Show as a table</summary>
            <TableWrap label="Page views per day">
              <thead>
                <tr>
                  <th scope="col" className={th}>
                    Day
                  </th>
                  <th scope="col" className={`${th} text-right`}>
                    Views
                  </th>
                </tr>
              </thead>
              <tbody>
                {[...data.series].reverse().map((d) => (
                  <tr key={d.day}>
                    <td className={td}>{formatBusinessDate(d.day)}</td>
                    <td className={`${td} num text-right`}>{formatNumber(d.count)}</td>
                  </tr>
                ))}
              </tbody>
            </TableWrap>
          </details>
        </div>

        <div className="grid gap-8 md:grid-cols-2">
          <RankedList title="Top pages" rows={data.topPaths} empty="No page views yet." />
          <RankedList title="Top referrers" rows={data.topReferrers} empty="No external referrers yet — visits so far came direct or from our own pages." />
        </div>
      </div>
    </Panel>
  );
}

function Figure({ label, value, hint }: { label: string; value: string; hint: string }) {
  return (
    <div className="rounded-2xl border border-line bg-night/40 p-4">
      <dt className="text-sm font-medium text-muted">{label}</dt>
      <dd className="num mt-1 font-display text-2xl font-bold text-fg">{value}</dd>
      <dd className="mt-1 text-xs text-subtle">{hint}</dd>
    </div>
  );
}

/** Label + count rows with a proportional bar (bars are decoration; the numbers carry the data). */
function RankedList({ title, rows, empty }: { title: string; rows: { label: string; count: number }[]; empty: string }) {
  const max = Math.max(1, ...rows.map((r) => r.count));
  return (
    <div>
      <h3 className="mb-3 text-sm font-semibold text-muted">{title}</h3>
      {rows.length === 0 ? (
        <p className="text-sm text-muted">{empty}</p>
      ) : (
        <ol className="flex flex-col gap-2">
          {rows.map((r) => (
            <li key={r.label}>
              <div className="flex items-baseline justify-between gap-3 text-sm">
                <span className="min-w-0 truncate font-medium text-fg">{r.label}</span>
                <span className="num shrink-0 font-semibold">{formatNumber(r.count)}</span>
              </div>
              <div className="mt-1 h-1.5 rounded-full bg-line/70" aria-hidden="true">
                <div className="h-full rounded-full bg-cyan" style={{ width: `${(r.count / max) * 100}%` }} />
              </div>
            </li>
          ))}
        </ol>
      )}
    </div>
  );
}
