import { redirect } from "next/navigation";
import { and, eq } from "drizzle-orm";
import { db } from "@db/client";
import { events, orders, tickets, users } from "@db/schema";
import { getCurrentRole } from "@/lib/auth";
import { getPlatformSettlements } from "@db/queries/payouts";
import { formatPrice } from "@/lib/format";
import { format } from "date-fns";
import { BarChart3, TrendingUp, Coins, Ticket, ShoppingBag } from "lucide-react";
import { PageHeader, StatCard } from "@/components/ui";

export const metadata = { title: "Reports · Admin" };

// Platform reports: GMV / fees / tickets over time, payment mix, and the top
// events + organizers. There's no RLS admin-override doing this implicitly
// anymore -- the caller must already be confirmed admin (see the gate below).
export default async function AdminReportsPage() {
  const { isAdmin } = await getCurrentRole();
  if (!isAdmin) redirect("/dashboard");

  const [allEvents, paid, allTickets, profiles] = await Promise.all([
    db.select({ id: events.id, organizerId: events.organizerId, title: events.title }).from(events),
    db
      .select({ eventId: orders.eventId, totalCents: orders.totalCents, createdAt: orders.createdAt, paymentMethod: orders.paymentMethod })
      .from(orders)
      .where(and(eq(orders.status, "paid"), eq(orders.currency, "kes"))),
    // Real tickets actually issued (one row per ticket via fulfillOrder),
    // not ticket_types.quantity_sold -- that column also carries decorative
    // "looks sold out" baseline numbers seeded from the hustle.events
    // import for storefront display, which isn't real sales data and
    // massively over-counted this stat (1,043 vs. 4 real tickets).
    db.select({ id: tickets.id }).from(tickets),
    db.select({ id: users.id, name: users.name, email: users.email }).from(users),
  ]);

  const titleByEvent = new Map(allEvents.map((e) => [e.id, e.title]));
  const orgByEvent = new Map(allEvents.map((e) => [e.id, e.organizerId]));
  const nameByOrg = new Map(profiles.map((p) => [p.id, p.name?.trim() || p.email]));

  const gmvCents = paid.reduce((sum, o) => sum + o.totalCents, 0);
  // Everything VibePass deducts (commission, its tax, messaging) -- same
  // numbers as /admin/earnings and payouts.
  const feeCents = (await getPlatformSettlements()).reduce((sum, s) => sum + s.deductions_cents, 0);
  const ticketsSold = allTickets.length;
  // VibePass is KES-only (see AGENTS.md) -- pre-migration Stripe/USD orders
  // are excluded from `paid` above so they can't get summed into this total.
  const currency = "kes";

  // GMV by month (chronological).
  const byMonth = new Map<string, { label: string; cents: number }>();
  for (const o of paid) {
    const key = format(o.createdAt, "yyyy-MM");
    const entry = byMonth.get(key) ?? { label: format(o.createdAt, "MMM yyyy"), cents: 0 };
    entry.cents += o.totalCents;
    byMonth.set(key, entry);
  }
  const months = [...byMonth.entries()].sort(([a], [b]) => a.localeCompare(b)).map(([, v]) => v);

  // Payment mix.
  const byMethod = new Map<string, number>();
  for (const o of paid) byMethod.set(o.paymentMethod, (byMethod.get(o.paymentMethod) ?? 0) + 1);
  const methods = [...byMethod.entries()].map(([m, count]) => ({
    label: m === "mpesa" ? "M-Pesa" : m === "paypal" ? "PayPal" : "Card",
    count,
  }));

  // Top events + organizers by gross.
  const grossByEvent = new Map<string, number>();
  const grossByOrg = new Map<string, number>();
  for (const o of paid) {
    grossByEvent.set(o.eventId, (grossByEvent.get(o.eventId) ?? 0) + o.totalCents);
    const org = orgByEvent.get(o.eventId);
    if (org) grossByOrg.set(org, (grossByOrg.get(org) ?? 0) + o.totalCents);
  }
  const topEvents = [...grossByEvent.entries()]
    .sort(([, a], [, b]) => b - a)
    .slice(0, 8)
    .map(([id, cents]) => ({ label: titleByEvent.get(id) ?? "—", cents }));
  const topOrganizers = [...grossByOrg.entries()]
    .sort(([, a], [, b]) => b - a)
    .slice(0, 8)
    .map(([id, cents]) => ({ label: nameByOrg.get(id) || "Unnamed", cents }));

  return (
    <div className="space-y-8">
      <PageHeader
        icon={BarChart3}
        title="Reports"
        description="Platform-wide performance across all organizers."
      />

      <section className="grid grid-cols-2 sm:grid-cols-4 gap-3">
        <StatCard icon={TrendingUp} label="GMV" value={formatPrice(gmvCents, currency)} />
        <StatCard icon={Coins} label="Fees collected" value={formatPrice(feeCents, currency)} accent="gold" />
        <StatCard icon={Ticket} label="Tickets sold" value={ticketsSold.toLocaleString()} />
        <StatCard icon={ShoppingBag} label="Paid orders" value={paid.length.toLocaleString()} />
      </section>

      <Panel title="GMV by month">
        {months.length === 0 ? (
          <Empty />
        ) : (
          <BarList items={months} currency={currency} />
        )}
      </Panel>

      <div className="grid gap-6 lg:grid-cols-2">
        <Panel title="Payment mix">
          {methods.length === 0 ? (
            <Empty />
          ) : (
            <div className="space-y-2">
              {methods.map((m) => (
                <div key={m.label} className="flex items-center justify-between text-sm text-be-text">
                  <span>{m.label}</span>
                  <span className="tabular-nums text-be-muted">
                    {m.count} ({Math.round((m.count / paid.length) * 100)}%)
                  </span>
                </div>
              ))}
            </div>
          )}
        </Panel>
        <Panel title="Top organizers">
          {topOrganizers.length === 0 ? <Empty /> : <BarList items={topOrganizers} currency={currency} />}
        </Panel>
      </div>

      <Panel title="Top events">
        {topEvents.length === 0 ? <Empty /> : <BarList items={topEvents} currency={currency} />}
      </Panel>
    </div>
  );
}

function BarList({
  items,
  currency,
}: {
  items: { label: string; cents: number }[];
  currency: string;
}) {
  const max = Math.max(...items.map((i) => i.cents), 1);
  return (
    <div className="space-y-2">
      {items.map((i) => (
        <div key={i.label} className="flex items-center gap-3 text-sm">
          <span className="w-40 shrink-0 truncate text-be-muted" title={i.label}>
            {i.label}
          </span>
          <div className="h-5 flex-1 overflow-hidden rounded bg-be-surface-2">
            <div
              className="h-full rounded bg-gradient-to-r from-brand-green to-brand-green-h"
              style={{ width: `${Math.max((i.cents / max) * 100, 2)}%` }}
            />
          </div>
          <span className="w-24 shrink-0 text-right tabular-nums text-be-text">
            {formatPrice(i.cents, currency)}
          </span>
        </div>
      ))}
    </div>
  );
}

function Panel({ title, children }: { title: string; children: React.ReactNode }) {
  return (
    <section className="be-card p-5">
      <h2 className="text-sm font-semibold mb-4 text-be-text">{title}</h2>
      {children}
    </section>
  );
}

function Empty() {
  return <p className="text-sm text-be-muted">No data yet.</p>;
}
