"use client";

import { useCallback, useEffect, useState } from "react";

import { api, downloadUrl, type DashboardStats, type EventStats } from "@/lib/api/browser";

import { Card, Empty, ErrorNote, Loading, formatDate } from "./ui";

/**
 * Dashboard overview (task 3.5, spec admin-dashboard).
 *
 * Three event cards plus an aggregate row, refreshed every 30 seconds. The refresh is
 * silent: replacing the cards with a spinner every half minute would make the numbers
 * unreadable exactly when the host is watching them come in.
 *
 * Response rates arrive from the API already scaled to 0-100. Multiplying by 100 here —
 * the obvious-looking thing to do with something called a rate — renders "10000%".
 */
const REFRESH_MS = 30_000;

export default function DashboardView({ canExport }: { canExport: boolean }) {
  const [stats, setStats] = useState<DashboardStats | null>(null);
  const [error, setError] = useState<unknown>(null);

  const load = useCallback(async () => {
    try {
      setStats(await api.stats());
      setError(null);
    } catch (err) {
      setError(err);
    }
  }, []);

  useEffect(() => {
    void load();
    const timer = setInterval(() => void load(), REFRESH_MS);
    // Polling a hidden tab burns the guest's battery and the server's connections for
    // numbers nobody is reading.
    const onVisible = () => document.visibilityState === "visible" && void load();
    document.addEventListener("visibilitychange", onVisible);
    return () => {
      clearInterval(timer);
      document.removeEventListener("visibilitychange", onVisible);
    };
  }, [load]);

  if (error && !stats) return <ErrorNote error={error} />;
  if (!stats) return <Loading label="Loading dashboard…" />;

  return (
    <div className="space-y-4">
      <ErrorNote error={error} />

      <div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
        {stats.events.map((event) => (
          <EventCard key={event.event_id} event={event} />
        ))}
      </div>

      <Card
        title={stats.owns_nothing ? "Your events" : "All events"}
        actions={
          canExport && (
            <a
              href={downloadUrl.export(new URLSearchParams())}
              className="inline-flex min-h-11 items-center rounded-md border border-stone-300 bg-white px-4 text-sm font-medium text-stone-800 hover:bg-stone-50"
            >
              Export CSV
            </a>
          )
        }
      >
        {stats.owns_nothing ? (
          // Not a row of zeroes (spec admin-dashboard). To a Host who owns nothing, zeroed
          // counts read as "the system is empty" when it is merely empty *for them* — and
          // the next thing they need is a link to create an event, not a set of totals.
          <Empty>
            You do not own any events yet.{" "}
            <a href="/admin/events" className="font-medium underline underline-offset-2">
              Create one
            </a>{" "}
            to get started, or ask a super admin to transfer one to you.
          </Empty>
        ) : stats.events.length === 0 ? (
          <Empty>No events yet. Seed the database or create one under Events.</Empty>
        ) : (
          <dl className="grid grid-cols-2 gap-4 sm:grid-cols-3">
            {/* Both figures span events, so both count records rather than people: a guest
                invited to three ceremonies is three records (design D11). Labelling them as
                people would overstate the guest list by however many people attend more
                than one ceremony — which at a wedding is most of them. */}
            <Stat
              label="Guest records"
              value={stats.aggregate.guest_records}
              hint="One per guest per event, not unique people"
            />
            <Stat
              label="Total headcount"
              value={stats.aggregate.total_headcount}
              hint="Seats across all events, counted once per event"
            />
            <Stat
              label="Response rate"
              value={`${Math.round(stats.aggregate.overall_response_rate)}%`}
            />
          </dl>
        )}
        <p className="mt-4 text-xs text-stone-400">
          Updated {new Date(stats.generated_at).toLocaleTimeString("en-GB")} · refreshes every
          30s
        </p>
      </Card>
    </div>
  );
}

function EventCard({ event }: { event: EventStats }) {
  const responded = event.accepted + event.declined;

  return (
    <Card>
      <div className="flex items-baseline justify-between gap-2">
        <h3 className="text-base font-semibold text-stone-900">{event.title_en}</h3>
        <Countdown days={event.days_to_event} />
      </div>
      <p className="mt-0.5 text-xs text-stone-500">{formatDate(event.starts_at)}</p>

      <dl className="mt-4 grid grid-cols-3 gap-2 text-center">
        <MiniStat label="Accepted" value={event.accepted} tone="good" />
        <MiniStat label="Pending" value={event.pending} />
        <MiniStat label="Declined" value={event.declined} />
      </dl>

      <div className="mt-4 space-y-2 border-t border-stone-100 pt-3 text-sm">
        <Row label="Headcount">
          <span className="font-semibold">{event.headcount}</span>
          {event.capacity != null && (
            <span className="text-stone-500"> / {event.capacity}</span>
          )}
        </Row>
        <Row label="Response rate">
          {Math.round(event.response_rate)}%
          <span className="text-stone-400"> ({responded}/{event.invited})</span>
        </Row>
        {/* v1 is email-only, so a guest with no address will never be reminded. Surfacing
            the count here is what stops that gap being discovered after the wedding. */}
        {event.no_email_count > 0 && (
          <Row label="No email">
            <span className="text-amber-700">{event.no_email_count}</span>
          </Row>
        )}
      </div>

      {event.over_capacity && (
        <p
          role="alert"
          className="mt-3 rounded-md bg-red-50 px-2 py-1.5 text-xs font-medium text-red-800"
        >
          Over capacity by {event.headcount - (event.capacity ?? 0)}
        </p>
      )}
    </Card>
  );
}

function Countdown({ days }: { days: number }) {
  if (days < 0) return <span className="text-xs text-stone-400">Past</span>;
  if (days === 0)
    return <span className="text-xs font-semibold text-amber-700">Today</span>;
  return (
    <span className="text-xs text-stone-500">
      {days} day{days === 1 ? "" : "s"}
    </span>
  );
}

function Row({ label, children }: { label: string; children: React.ReactNode }) {
  return (
    <div className="flex justify-between">
      <dt className="text-stone-500">{label}</dt>
      <dd>{children}</dd>
    </div>
  );
}

function MiniStat({
  label,
  value,
  tone,
}: {
  label: string;
  value: number;
  tone?: "good";
}) {
  return (
    <div className="rounded-lg bg-stone-50 py-2">
      <dd
        className={`text-lg font-semibold ${tone === "good" ? "text-emerald-700" : "text-stone-900"}`}
      >
        {value}
      </dd>
      <dt className="text-[0.7rem] uppercase tracking-wide text-stone-500">{label}</dt>
    </div>
  );
}

function Stat({
  label,
  value,
  hint,
}: {
  label: string;
  value: number | string;
  hint?: string;
}) {
  return (
    <div>
      <dd className="text-2xl font-semibold text-stone-900">{value}</dd>
      <dt className="text-xs text-stone-500">{label}</dt>
      {hint && <p className="text-[0.7rem] text-stone-400">{hint}</p>}
    </div>
  );
}
