// app/admin/page.tsx
// Admin dashboard home (admin.md section 2.1). Per-event, with an event switcher.
// Live entries + revenue with by-channel and by-rail breakdowns and a per-minute
// sparkline. All pool figures derive from PAID purchases + non-void tickets.
//
// Defense in depth: this page calls requireRole("viewer") itself (the layout
// renders bare when unauthenticated), and redirects to /admin/login on failure.

import * as React from "react";
import { redirect } from "next/navigation";
import { requireRole, getSession, roleSatisfies, AuthError } from "@/lib/auth";
import {
  listRaffles,
  getDashboardSummary,
  getByChannel,
  getByRail,
  getPerMinutePaid,
} from "@/app/admin/_lib/queries";
import { getRaffleConfig } from "@/app/admin/_lib/config";
import { Card, ImpactStat } from "@/components/ui";
import { RaffleSwitcher } from "@/components/admin/RaffleSwitcher";
import { Sparkline } from "@/components/admin/Sparkline";
import { DashboardAutoRefresh } from "@/components/admin/DashboardAutoRefresh";
import { formatPEN } from "@/lib/money";

export const dynamic = "force-dynamic";

export default async function AdminDashboardPage({
  searchParams,
}: {
  searchParams: Promise<{ raffle?: string }>;
}) {
  try {
    await requireRole("viewer");
  } catch (err) {
    if (err instanceof AuthError) redirect("/admin/login");
    throw err;
  }
  const session = await getSession();
  const canExportEntries = session ? roleSatisfies(session.role, "operator") : false;

  const raffles = await listRaffles();
  if (raffles.length === 0) {
    return (
      <div>
        <h1 className="font-impact text-heading-lg font-bold text-ink-strong">
          Panel
        </h1>
        <p className="mt-4 font-body text-body-lg text-muted">
          Aún no hay sorteos creados. Crea un sorteo para ver datos aquí.
        </p>
      </div>
    );
  }

  const { raffle: raffleParam } = await searchParams;
  const selected = raffles.find((r) => r.id === raffleParam) ?? raffles[0];

  const [summary, channels, rails, spark, config] = await Promise.all([
    getDashboardSummary(selected.id),
    getByChannel(selected.id),
    getByRail(selected.id),
    getPerMinutePaid(selected.id, 120),
    getRaffleConfig(selected.id),
  ]);

  const netEstimateNote =
    "Bruto confirmado. El neto después de comisiones se calcula en la exportación de recaudación (comisión estimada).";

  return (
    <div className="flex flex-col gap-lg">
      <DashboardAutoRefresh seconds={25} />

      <div className="flex flex-wrap items-center justify-between gap-md">
        <h1 className="font-impact text-heading-lg font-bold text-ink-strong">
          Panel
        </h1>
        <RaffleSwitcher
          raffles={raffles.map((r) => ({
            id: r.id,
            name: r.name,
            status: r.status,
          }))}
          selectedId={selected.id}
        />
      </div>

      <p className="font-body text-caption text-muted" aria-live="polite">
        Sorteo: <strong>{selected.name}</strong> · estado {selected.status} ·
        actualiza cada 25 segundos.
      </p>

      {/* Headline tiles: pool truth */}
      <section aria-label="Resumen del pool" className="grid grid-cols-1 gap-md sm:grid-cols-2 lg:grid-cols-4">
        <Card elevation="drop" className="p-lg">
          <ImpactStat
            figure={summary.activeTickets.toLocaleString("es-PE")}
            label="Oportunidades en el pool (tickets activos)"
          />
        </Card>
        <Card elevation="drop" className="p-lg">
          <ImpactStat
            figure={summary.confirmedUniqueParticipants.toLocaleString("es-PE")}
            label="Participantes confirmados únicos"
          />
        </Card>
        <Card elevation="drop" className="p-lg">
          <ImpactStat
            figure={formatPEN(summary.grossConfirmedCents)}
            label="Recaudación bruta confirmada"
          />
        </Card>
        <Card elevation="drop" className="p-lg">
          <ImpactStat
            figure={formatPEN(summary.feeCoveredCents)}
            label="Comisión cubierta por donantes"
          />
        </Card>
      </section>
      <p className="font-body text-caption text-muted">{netEstimateNote}</p>

      {/* Secondary tiles: pipeline + risk signals (never in the pool) */}
      <section
        aria-label="Estados que no entran al pool"
        className="grid grid-cols-2 gap-md sm:grid-cols-3 lg:grid-cols-6"
      >
        <MiniStat label="Pendientes" value={summary.pendingCount} note="No entran al pool" />
        <MiniStat label="Monto pendiente" value={formatPEN(summary.pendingAmountCents)} />
        <MiniStat label="Fallidas 24h" value={summary.failedLast24h} note="Posible carding" />
        <MiniStat label="Expiradas 24h" value={summary.expiredLast24h} />
        <MiniStat label="Reembolsadas" value={summary.refundedCount} />
        <MiniStat label="Monto reembolsado" value={formatPEN(summary.refundedAmountCents)} />
      </section>

      {/* Publish-gate banner */}
      {selected.status === "draft" ? (
        <Card elevation="tight" className="p-lg">
          <h2 className="font-display text-heading-md font-bold text-ink-strong">
            Estado de habilitación
          </h2>
          <ul className="mt-3 flex flex-col gap-1.5 font-body text-body-md text-ink-strong">
            <GateItem ok={Boolean(config.mininterResolutionRef)}>
              Resolución MININTER: {config.mininterResolutionRef ?? "pendiente"}
            </GateItem>
            <GateItem ok={Boolean(config.pricingFrozenAt)}>
              Precio congelado: {config.pricingFrozenAt ? "sí" : "no"}
            </GateItem>
            <GateItem ok={Boolean(selected.basesUrl)}>
              PDF de bases: {selected.basesUrl ? "cargado" : "pendiente"}
            </GateItem>
            <GateItem ok={config.basesPublished}>
              Bases publicadas: {config.basesPublished ? "sí" : "no"}
            </GateItem>
          </ul>
          <p className="mt-3 font-body text-caption text-muted">
            Abre el sorteo desde Configuración una vez que se cumplan todos los requisitos.
          </p>
        </Card>
      ) : null}

      {/* Sparkline */}
      <Card elevation="tight" className="p-lg">
        <h2 className="font-display text-heading-md font-bold text-ink-strong">
          Compras pagadas por minuto (últimas 2 horas)
        </h2>
        <div className="mt-4">
          <Sparkline points={spark} />
        </div>
      </Card>

      {/* Exports */}
      <Card elevation="tight" className="p-lg">
        <h2 className="font-display text-heading-md font-bold text-ink-strong">
          Exportar
        </h2>
        <div className="mt-4 flex flex-wrap gap-3">
          <a
            href={`/api/admin/raffles/${selected.id}/revenue.csv`}
            className="inline-flex min-h-11 items-center rounded-md border border-steelblue px-4 font-body text-body-md font-bold text-steelblue hover:bg-steelblue hover:text-on-primary focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-steelblue"
          >
            Recaudación (CSV)
          </a>
          {canExportEntries ? (
            <>
              <a
                href={`/api/admin/raffles/${selected.id}/entries.csv`}
                className="inline-flex min-h-11 items-center rounded-md border border-steelblue px-4 font-body text-body-md font-bold text-steelblue hover:bg-steelblue hover:text-on-primary focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-steelblue"
              >
                Entradas confirmadas (CSV, enmascarado)
              </a>
              <a
                href={`/api/admin/raffles/${selected.id}/entries.csv?unmask=1`}
                className="inline-flex min-h-11 items-center rounded-md border border-primary px-4 font-body text-body-md font-bold text-primary hover:bg-primary hover:text-on-primary focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary"
              >
                Entradas con datos completos (CSV, registrado)
              </a>
            </>
          ) : null}
        </div>
        <p className="mt-3 font-body text-caption text-muted">
          La recaudación es agregada (sin datos personales). La exportación de
          entradas con datos completos queda registrada en el log de auditoría.
        </p>
      </Card>

      {/* By-channel + by-rail */}
      <div className="grid grid-cols-1 gap-lg lg:grid-cols-2">
        <Card elevation="tight" className="p-lg">
          <h2 className="font-display text-heading-md font-bold text-ink-strong">
            Por canal
          </h2>
          <BreakdownTable
            caption="Compras pagadas por canal (UTM / código de influencer)"
            head={["Canal", "Compras", "Oportunidades", "Bruto"]}
            rows={channels.map((c) => [
              c.channel,
              c.paidCount.toLocaleString("es-PE"),
              c.chances.toLocaleString("es-PE"),
              formatPEN(c.grossCents),
            ])}
          />
        </Card>
        <Card elevation="tight" className="p-lg">
          <h2 className="font-display text-heading-md font-bold text-ink-strong">
            Por medio de pago
          </h2>
          <BreakdownTable
            caption="Compras pagadas por medio (Yape / tarjeta / PagoEfectivo / manual)"
            head={["Medio", "Compras", "Oportunidades", "Bruto"]}
            rows={rails.map((r) => [
              railLabel(r.method),
              r.paidCount.toLocaleString("es-PE"),
              r.chances.toLocaleString("es-PE"),
              formatPEN(r.grossCents),
            ])}
          />
        </Card>
      </div>
    </div>
  );
}

function MiniStat({
  label,
  value,
  note,
}: {
  label: string;
  value: number | string;
  note?: string;
}) {
  return (
    <div className="rounded-md border border-hairline bg-canvas p-4">
      <p className="font-impact text-heading-md font-bold text-ink-strong">
        {typeof value === "number" ? value.toLocaleString("es-PE") : value}
      </p>
      <p className="mt-1 font-body text-caption text-muted">{label}</p>
      {note ? (
        <p className="mt-0.5 font-body text-micro text-muted">{note}</p>
      ) : null}
    </div>
  );
}

function GateItem({ ok, children }: { ok: boolean; children: React.ReactNode }) {
  return (
    <li className="flex items-start gap-2">
      <span aria-hidden="true" className={ok ? "text-steelblue" : "text-primary"}>
        {ok ? "✓" : "✗"}
      </span>
      <span>
        <span className="sr-only">{ok ? "Cumplido: " : "Pendiente: "}</span>
        {children}
      </span>
    </li>
  );
}

function BreakdownTable({
  caption,
  head,
  rows,
}: {
  caption: string;
  head: string[];
  rows: (string | number)[][];
}) {
  return (
    <div className="mt-4 overflow-x-auto">
      <table className="w-full border-collapse text-left">
        <caption className="sr-only">{caption}</caption>
        <thead>
          <tr className="border-b border-hairline">
            {head.map((h) => (
              <th
                key={h}
                scope="col"
                className="py-2 pr-4 font-body text-caption font-medium text-muted-label"
              >
                {h}
              </th>
            ))}
          </tr>
        </thead>
        <tbody>
          {rows.length === 0 ? (
            <tr>
              <td
                colSpan={head.length}
                className="py-3 font-body text-body-md text-muted"
              >
                Sin datos todavía.
              </td>
            </tr>
          ) : (
            rows.map((row, i) => (
              <tr key={i} className="border-b border-hairline last:border-0">
                {row.map((cell, j) => (
                  <td
                    key={j}
                    className="py-2 pr-4 font-body text-body-md text-ink-strong"
                  >
                    {cell}
                  </td>
                ))}
              </tr>
            ))
          )}
        </tbody>
      </table>
    </div>
  );
}

function railLabel(method: string): string {
  switch (method) {
    case "yape":
      return "Yape";
    case "card":
      return "Tarjeta";
    case "pago_efectivo":
      return "PagoEfectivo";
    case "manual":
      return "Manual";
    default:
      return "Otro";
  }
}
