// @File: src/pages/ClientUptime.tsx
// @Desc: Client-facing Uptime Monitoring page. Monitor list on the left,
//        selected monitor detail + recent incident timeline on the right.
//        Supports `?monitor=<id>` so the ClientDashboard sidebar tooltip
//        can deep-link directly to a specific monitor.
import { useCallback, useEffect, useMemo, useState } from "react";
import { Link, useSearchParams } from "react-router-dom";
import {
  Activity,
  AlertTriangle,
  ArrowLeft,
  BellOff,
  BellRing,
  CheckCircle2,
  Clock,
  Loader2,
  Mail,
  PauseCircle,
  RefreshCw,
  Rocket,
  Sparkles,
  XCircle,
} from "lucide-react";

import {
  clientActivateSubscription,
  clientGetDashboard,
  clientGetNotificationPrefs,
  clientListAvailablePackages,
  clientListMonitors,
  clientMonitorLogs,
  clientUpdateNotificationPrefs,
  type UptimeClientDashboard,
  type UptimeMonitor,
  type UptimeNotificationPref,
  type UptimePackage,
} from "@/lib/uptimerobotApi";
import { toast } from "sonner";
import { useUserProfile } from "@/hooks/useUserProfile";

// ---------- helpers ----------
type UptimeKind = "up" | "down" | "paused" | "unknown";

function classifyStatus(status: string): UptimeKind {
  const s = (status || "").toLowerCase();
  if (s.includes("paus")) return "paused";
  if (s.includes("up") || s === "ok" || s === "online") return "up";
  if (s.includes("down") || s.includes("err") || s === "offline") return "down";
  return "unknown";
}

function kindOf(monitor: UptimeMonitor): UptimeKind {
  if (monitor.paused) return "paused";
  return classifyStatus(monitor.status || "");
}

function kindTone(kind: UptimeKind): {
  dot: string;
  pill: string;
  label: string;
  Icon: typeof CheckCircle2;
} {
  switch (kind) {
    case "up":
      return {
        dot: "bg-emerald-400",
        pill: "bg-emerald-500/15 text-emerald-300",
        label: "Up",
        Icon: CheckCircle2,
      };
    case "down":
      return {
        dot: "bg-rose-400",
        pill: "bg-rose-500/20 text-rose-300",
        label: "Down",
        Icon: XCircle,
      };
    case "paused":
      return {
        dot: "bg-amber-400",
        pill: "bg-amber-500/15 text-amber-300",
        label: "Paused",
        Icon: PauseCircle,
      };
    default:
      return {
        dot: "bg-slate-400",
        pill: "bg-white/10 text-slate-300",
        label: "Unknown",
        Icon: Activity,
      };
  }
}

function formatDateTime(value: string | number | null | undefined): string {
  if (value === null || value === undefined || value === "") return "—";
  let ms: number | null = null;
  if (typeof value === "number") {
    // UptimeRobot uses seconds; if value looks like seconds (<= 10^11) scale up.
    ms = value < 1e12 ? value * 1000 : value;
  } else {
    const parsed = Date.parse(value);
    if (Number.isFinite(parsed)) ms = parsed;
    else {
      const asNum = Number(value);
      if (Number.isFinite(asNum)) ms = asNum < 1e12 ? asNum * 1000 : asNum;
    }
  }
  if (!ms) return String(value);
  try {
    return new Date(ms).toLocaleString();
  } catch {
    return String(value);
  }
}

function formatDuration(seconds: number | null | undefined): string {
  if (seconds === null || seconds === undefined || !Number.isFinite(Number(seconds))) {
    return "—";
  }
  const total = Math.max(0, Math.floor(Number(seconds)));
  if (total < 60) return `${total}s`;
  const mins = Math.floor(total / 60);
  const secs = total % 60;
  if (mins < 60) return secs ? `${mins}m ${secs}s` : `${mins}m`;
  const hours = Math.floor(mins / 60);
  const remMins = mins % 60;
  if (hours < 24) return remMins ? `${hours}h ${remMins}m` : `${hours}h`;
  const days = Math.floor(hours / 24);
  const remHours = hours % 24;
  return remHours ? `${days}d ${remHours}h` : `${days}d`;
}

// Map UptimeRobot log `type` codes to a friendlier incident label.
// Ref: https://uptimerobot.com/api/ — 1=down, 2=up, 98=started, 99=paused.
function incidentLabel(type: unknown): { text: string; kind: UptimeKind } {
  const code = typeof type === "number" ? type : Number(type);
  if (code === 1) return { text: "Went down", kind: "down" };
  if (code === 2) return { text: "Came back up", kind: "up" };
  if (code === 98) return { text: "Monitoring started", kind: "up" };
  if (code === 99) return { text: "Paused", kind: "paused" };
  if (typeof type === "string") {
    const s = type.toLowerCase();
    if (s.includes("down")) return { text: "Went down", kind: "down" };
    if (s.includes("up")) return { text: "Came back up", kind: "up" };
    if (s.includes("paus")) return { text: "Paused", kind: "paused" };
    if (s.includes("start")) return { text: "Monitoring started", kind: "up" };
  }
  return { text: "Event", kind: "unknown" };
}

interface NormalizedIncident {
  id: string;
  type: unknown;
  at: string | number | null;
  duration: number | null;
  reason: string | null;
}

// Try to normalize the upstream logs payload. UptimeRobot's v3 response
// usually looks like { logs: [...] } or { data: { logs: [...] } }; we defensively
// handle either shape plus a plain array.
function normalizeLogs(payload: unknown): NormalizedIncident[] {
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  const any = payload as any;
  let raw: unknown = null;
  if (Array.isArray(any)) raw = any;
  else if (any && Array.isArray(any.logs)) raw = any.logs;
  else if (any && any.data && Array.isArray(any.data.logs)) raw = any.data.logs;
  else if (any && any.data && Array.isArray(any.data)) raw = any.data;
  else if (any && Array.isArray(any.items)) raw = any.items;

  if (!Array.isArray(raw)) return [];

  return raw.map((entry, idx) => {
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    const e = entry as any;
    const at =
      e?.datetime ??
      e?.date ??
      e?.created_at ??
      e?.timestamp ??
      e?.time ??
      null;
    const durationRaw = e?.duration ?? e?.duration_seconds ?? null;
    const duration =
      durationRaw === null || durationRaw === undefined
        ? null
        : Number(durationRaw);
    return {
      id: String(e?.id ?? `${at ?? "log"}-${idx}`),
      type: e?.type ?? e?.status ?? e?.event ?? null,
      at,
      duration: Number.isFinite(duration as number) ? (duration as number) : null,
      reason:
        (typeof e?.reason === "string" && e.reason) ||
        (typeof e?.reason?.detail === "string" && e.reason.detail) ||
        (typeof e?.message === "string" && e.message) ||
        null,
    };
  });
}

// ---------- component ----------
export default function ClientUptime() {
  const { authed, loading: authLoading } = useUserProfile();
  const isAuthenticated = authed;
  const [searchParams, setSearchParams] = useSearchParams();

  const [dashboard, setDashboard] = useState<UptimeClientDashboard | null>(null);
  const [monitors, setMonitors] = useState<UptimeMonitor[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const [refreshing, setRefreshing] = useState(false);

  const [selectedId, setSelectedId] = useState<number | null>(null);
  const [incidents, setIncidents] = useState<NormalizedIncident[]>([]);
  const [incidentsLoading, setIncidentsLoading] = useState(false);
  const [incidentsError, setIncidentsError] = useState<string | null>(null);

  // Email-on-status-change toggle.
  const [notifPref, setNotifPref] = useState<UptimeNotificationPref | null>(null);
  const [notifSaving, setNotifSaving] = useState(false);
  const [notifError, setNotifError] = useState<string | null>(null);

  // Available packages for self-service signup.
  const [availablePackages, setAvailablePackages] = useState<UptimePackage[]>([]);
  const [packagesLoading, setPackagesLoading] = useState(false);
  const [activatingPackageId, setActivatingPackageId] = useState<number | null>(
    null,
  );

  // Load monitors + subscription summary.
  const loadAll = useCallback(async () => {
    setError(null);
    try {
      const [dash, mons] = await Promise.all([
        clientGetDashboard().catch(() => null),
        clientListMonitors().catch(() => [] as UptimeMonitor[]),
      ]);
      setDashboard(dash);
      setMonitors(mons);
    } catch (err) {
      setError(err instanceof Error ? err.message : "Failed to load monitors.");
    } finally {
      setLoading(false);
      setRefreshing(false);
    }
  }, []);

  // Load available packages only when the user has no active subscription
  // so we don't waste a round trip for already-subscribed clients.
  const hasSubscription = !!dashboard?.subscription;
  useEffect(() => {
    if (!isAuthenticated) return;
    if (loading) return;
    if (hasSubscription) return;
    let cancelled = false;
    setPackagesLoading(true);
    clientListAvailablePackages()
      .then((rows) => {
        if (!cancelled) setAvailablePackages(rows);
      })
      .catch(() => {
        if (!cancelled) setAvailablePackages([]);
      })
      .finally(() => {
        if (!cancelled) setPackagesLoading(false);
      });
    return () => {
      cancelled = true;
    };
  }, [isAuthenticated, loading, hasSubscription]);

  const handleActivate = useCallback(
    async (packageId: number) => {
      if (activatingPackageId !== null) return;
      setActivatingPackageId(packageId);
      try {
        await clientActivateSubscription(packageId);
        toast.success("Subscription activated. You can now add monitors.");
        await loadAll();
      } catch (err) {
        toast.error(
          err instanceof Error
            ? err.message
            : "Could not activate subscription.",
        );
      } finally {
        setActivatingPackageId(null);
      }
    },
    [activatingPackageId, loadAll],
  );

  useEffect(() => {
    if (!isAuthenticated) return;
    void loadAll();
  }, [isAuthenticated, loadAll]);

  // Load email-notification toggle state. Runs independently from the monitor
  // list so a backend hiccup on one side doesn't hide the other.
  useEffect(() => {
    if (!isAuthenticated) return;
    let cancelled = false;
    clientGetNotificationPrefs()
      .then((pref) => {
        if (!cancelled) setNotifPref(pref);
      })
      .catch((err: unknown) => {
        if (cancelled) return;
        setNotifError(
          err instanceof Error ? err.message : "Failed to load notification settings.",
        );
      });
    return () => {
      cancelled = true;
    };
  }, [isAuthenticated]);

  const handleToggleNotifications = useCallback(async () => {
    if (notifSaving) return;
    const nextEnabled = !(notifPref?.email_notifications_enabled ?? false);
    setNotifSaving(true);
    setNotifError(null);
    // Optimistic update so the switch feels instant.
    setNotifPref((prev) =>
      prev
        ? { ...prev, email_notifications_enabled: nextEnabled }
        : { email_notifications_enabled: nextEnabled, notify_email: null },
    );
    try {
      const updated = await clientUpdateNotificationPrefs(nextEnabled);
      setNotifPref(updated);
    } catch (err) {
      // Roll back on failure.
      setNotifPref((prev) =>
        prev
          ? { ...prev, email_notifications_enabled: !nextEnabled }
          : prev,
      );
      setNotifError(
        err instanceof Error ? err.message : "Could not update notification settings.",
      );
    } finally {
      setNotifSaving(false);
    }
  }, [notifPref, notifSaving]);

  // Sync ?monitor=<id> → selectedId. Fall back to first monitor if none provided.
  useEffect(() => {
    if (monitors.length === 0) {
      setSelectedId(null);
      return;
    }
    const param = searchParams.get("monitor");
    if (param) {
      const n = Number(param);
      const found = monitors.find((m) => m.id === n);
      if (found) {
        setSelectedId(found.id);
        return;
      }
    }
    // Keep current if still valid, otherwise pick first.
    if (selectedId && monitors.some((m) => m.id === selectedId)) return;
    setSelectedId(monitors[0].id);
  }, [monitors, searchParams, selectedId]);

  const selected = useMemo(
    () => monitors.find((m) => m.id === selectedId) ?? null,
    [monitors, selectedId],
  );

  // Load incidents whenever the selected monitor changes.
  useEffect(() => {
    if (!selected) {
      setIncidents([]);
      setIncidentsError(null);
      return;
    }
    let cancelled = false;
    setIncidentsLoading(true);
    setIncidentsError(null);
    clientMonitorLogs(selected.id, 20)
      .then((payload) => {
        if (cancelled) return;
        setIncidents(normalizeLogs(payload));
      })
      .catch((err: unknown) => {
        if (cancelled) return;
        setIncidents([]);
        setIncidentsError(
          err instanceof Error ? err.message : "Failed to load incident history.",
        );
      })
      .finally(() => {
        if (!cancelled) setIncidentsLoading(false);
      });
    return () => {
      cancelled = true;
    };
  }, [selected]);

  const counts = useMemo(() => {
    let up = 0;
    let down = 0;
    let paused = 0;
    for (const m of monitors) {
      const k = kindOf(m);
      if (k === "up") up += 1;
      else if (k === "down") down += 1;
      else if (k === "paused") paused += 1;
    }
    return { up, down, paused, total: monitors.length };
  }, [monitors]);

  const handleSelect = (monitor: UptimeMonitor) => {
    setSelectedId(monitor.id);
    const next = new URLSearchParams(searchParams);
    next.set("monitor", String(monitor.id));
    setSearchParams(next, { replace: true });
  };

  const handleRefresh = () => {
    setRefreshing(true);
    void loadAll();
  };

  // ---------- auth states ----------
  if (authLoading) {
    return (
      <div className="min-h-screen flex items-center justify-center bg-slate-950 text-slate-200">
        <Loader2 className="w-6 h-6 animate-spin" />
      </div>
    );
  }
  if (!isAuthenticated) {
    return (
      <div className="min-h-screen flex items-center justify-center bg-slate-950 text-slate-200 px-6 text-center">
        <div>
          <p className="mb-4">You need to sign in to view your uptime monitors.</p>
          <Link to="/login" className="text-emerald-400 underline">
            Go to login
          </Link>
        </div>
      </div>
    );
  }

  // ---------- layout ----------
  return (
    <div className="min-h-screen bg-slate-950 text-slate-200">
      {/* Header */}
      <header className="border-b border-white/10 bg-slate-900/60 backdrop-blur">
        <div className="max-w-[1400px] mx-auto px-6 py-4 flex items-center gap-4">
          <Link
            to="/client"
            className="inline-flex items-center gap-2 text-sm text-slate-300 hover:text-white transition"
          >
            <ArrowLeft className="w-4 h-4" />
            Back to dashboard
          </Link>
          <div className="flex items-center gap-2 ml-auto">
            <h1 className="text-lg font-semibold text-white flex items-center gap-2">
              <Activity className="w-5 h-5 text-[#0b6e3e]" />
              Uptime Monitoring
            </h1>
            <button
              type="button"
              onClick={handleRefresh}
              disabled={refreshing || loading}
              className="inline-flex items-center gap-1.5 rounded-md border border-white/10 bg-white/5 px-3 py-1.5 text-xs text-slate-200 hover:bg-white/10 disabled:opacity-60"
            >
              <RefreshCw
                className={`w-3.5 h-3.5 ${refreshing ? "animate-spin" : ""}`}
              />
              Refresh
            </button>
          </div>
        </div>
      </header>

      <main className="max-w-[1400px] mx-auto px-6 py-6">
        {/* Self-service package picker: shown when the user has no active subscription. */}
        {!loading && !hasSubscription && (
          <PackagePicker
            packages={availablePackages}
            loading={packagesLoading}
            activatingPackageId={activatingPackageId}
            onActivate={handleActivate}
          />
        )}

        {/* Email notification toggle */}
        <NotificationToggle
          pref={notifPref}
          saving={notifSaving}
          error={notifError}
          onToggle={handleToggleNotifications}
        />

        {/* Top summary strip */}
        <section className="grid grid-cols-2 md:grid-cols-4 gap-3 mb-6">
          <SummaryCard label="Monitors" value={counts.total} tone="slate" />
          <SummaryCard label="Up" value={counts.up} tone="emerald" />
          <SummaryCard label="Down" value={counts.down} tone="rose" />
          <SummaryCard label="Paused" value={counts.paused} tone="amber" />
        </section>

        {error && (
          <div className="mb-4 rounded-lg border border-rose-500/30 bg-rose-500/10 px-4 py-3 text-sm text-rose-200">
            {error}
          </div>
        )}

        {/* List + detail */}
        <div className="grid grid-cols-1 lg:grid-cols-[340px_1fr] gap-6">
          {/* Monitor list */}
          <aside className="rounded-xl border border-white/10 bg-slate-900/40 overflow-hidden">
            <div className="px-4 py-3 border-b border-white/10 flex items-center justify-between">
              <h2 className="text-sm font-semibold text-white">Your monitors</h2>
              <span className="text-[11px] uppercase tracking-wide text-slate-400">
                {counts.total} total
              </span>
            </div>
            {loading ? (
              <div className="px-4 py-8 text-center text-sm text-slate-400">
                <Loader2 className="w-5 h-5 animate-spin mx-auto mb-2" />
                Loading monitors…
              </div>
            ) : monitors.length === 0 ? (
              <div className="px-4 py-8 text-center text-sm text-slate-400">
                No monitors yet.
                {dashboard?.subscription ? (
                  <p className="mt-2 text-xs">
                    Your subscription is active. Monitors you create will appear here.
                  </p>
                ) : (
                  <p className="mt-2 text-xs">
                    Pick a plan above to activate your uptime monitoring.
                  </p>
                )}
              </div>
            ) : (
              <ul className="max-h-[70vh] overflow-y-auto divide-y divide-white/5">
                {monitors.map((m) => {
                  const kind = kindOf(m);
                  const tone = kindTone(kind);
                  const active = m.id === selectedId;
                  return (
                    <li key={m.id}>
                      <button
                        type="button"
                        onClick={() => handleSelect(m)}
                        className={`w-full text-left px-4 py-3 transition flex items-start gap-3 ${
                          active
                            ? "bg-white/10"
                            : "hover:bg-white/5"
                        }`}
                      >
                        <span
                          className={`mt-1 inline-block h-2.5 w-2.5 rounded-full ${tone.dot}`}
                          aria-hidden
                        />
                        <span className="min-w-0 flex-1">
                          <span className="block truncate text-sm font-medium text-white">
                            {m.friendly_name || m.url || `Monitor #${m.id}`}
                          </span>
                          <span className="block truncate text-xs text-slate-400">
                            {m.url || m.monitor_type}
                          </span>
                        </span>
                        <span
                          className={`shrink-0 rounded-full px-2 py-0.5 text-[10px] uppercase tracking-wide ${tone.pill}`}
                        >
                          {tone.label}
                        </span>
                      </button>
                    </li>
                  );
                })}
              </ul>
            )}
          </aside>

          {/* Detail */}
          <section className="rounded-xl border border-white/10 bg-slate-900/40 overflow-hidden">
            {!selected ? (
              <div className="px-6 py-16 text-center text-sm text-slate-400">
                Select a monitor on the left to view details.
              </div>
            ) : (
              <MonitorDetail
                monitor={selected}
                incidents={incidents}
                incidentsLoading={incidentsLoading}
                incidentsError={incidentsError}
              />
            )}
          </section>
        </div>
      </main>
    </div>
  );
}

// ---------- subcomponents ----------
interface SummaryCardProps {
  label: string;
  value: number;
  tone: "slate" | "emerald" | "rose" | "amber";
}

function SummaryCard({ label, value, tone }: SummaryCardProps) {
  const palette = {
    slate: "border-white/10 bg-white/5 text-slate-200",
    emerald: "border-emerald-500/20 bg-emerald-500/10 text-emerald-200",
    rose: "border-rose-500/20 bg-rose-500/10 text-rose-200",
    amber: "border-amber-500/20 bg-amber-500/10 text-amber-200",
  }[tone];
  return (
    <div className={`rounded-xl border ${palette} px-4 py-3`}>
      <div className="text-[11px] uppercase tracking-wide opacity-80">{label}</div>
      <div className="text-2xl font-semibold mt-0.5">{value}</div>
    </div>
  );
}

interface MonitorDetailProps {
  monitor: UptimeMonitor;
  incidents: NormalizedIncident[];
  incidentsLoading: boolean;
  incidentsError: string | null;
}

function MonitorDetail({
  monitor,
  incidents,
  incidentsLoading,
  incidentsError,
}: MonitorDetailProps) {
  const kind = kindOf(monitor);
  const tone = kindTone(kind);
  const { Icon } = tone;

  return (
    <div>
      <div className="px-6 py-5 border-b border-white/10 flex flex-wrap items-start gap-4">
        <div className="min-w-0 flex-1">
          <div className="flex items-center gap-2 mb-1">
            <Icon className="w-4 h-4 text-slate-300" />
            <span
              className={`rounded-full px-2 py-0.5 text-[10px] uppercase tracking-wide ${tone.pill}`}
            >
              {tone.label}
            </span>
            <span className="text-xs text-slate-400">
              {monitor.monitor_type?.toUpperCase() || "MONITOR"}
            </span>
          </div>
          <h2 className="text-xl font-semibold text-white truncate">
            {monitor.friendly_name || monitor.url || `Monitor #${monitor.id}`}
          </h2>
          {monitor.url && (
            <a
              href={monitor.url}
              target="_blank"
              rel="noreferrer"
              className="text-xs text-emerald-300 hover:text-emerald-200 break-all"
            >
              {monitor.url}
            </a>
          )}
        </div>
      </div>

      {/* Stats */}
      <div className="px-6 py-5 grid grid-cols-2 md:grid-cols-4 gap-3 border-b border-white/10">
        <StatCard
          label="24h uptime"
          value={monitor.uptime_ratio_24h ? `${monitor.uptime_ratio_24h}%` : "—"}
        />
        <StatCard
          label="7d uptime"
          value={monitor.uptime_ratio_7d ? `${monitor.uptime_ratio_7d}%` : "—"}
        />
        <StatCard
          label="30d uptime"
          value={monitor.uptime_ratio_30d ? `${monitor.uptime_ratio_30d}%` : "—"}
        />
        <StatCard
          label="Response"
          value={
            monitor.response_time_ms !== null
              ? `${monitor.response_time_ms} ms`
              : "—"
          }
        />
      </div>

      {/* Meta */}
      <div className="px-6 py-5 grid grid-cols-1 md:grid-cols-2 gap-x-6 gap-y-2 text-sm border-b border-white/10">
        <MetaRow label="Check interval" value={`${monitor.interval_seconds}s`} />
        <MetaRow
          label="Keyword"
          value={monitor.keyword || (monitor.monitor_type === "keyword" ? "—" : null)}
        />
        <MetaRow
          label="Port"
          value={monitor.port !== null ? String(monitor.port) : null}
        />
        <MetaRow
          label="Last synced"
          value={formatDateTime(monitor.last_synced_at)}
        />
        {monitor.last_error && (
          <div className="md:col-span-2 flex items-start gap-2 rounded-md border border-rose-500/20 bg-rose-500/5 px-3 py-2 text-xs text-rose-200">
            <AlertTriangle className="w-4 h-4 shrink-0 mt-0.5" />
            <span>
              <span className="font-semibold">Last error:</span> {monitor.last_error}
            </span>
          </div>
        )}
      </div>

      {/* Incident timeline */}
      <div className="px-6 py-5">
        <div className="flex items-center justify-between mb-3">
          <h3 className="text-sm font-semibold text-white flex items-center gap-2">
            <Clock className="w-4 h-4 text-slate-400" />
            Recent incidents
          </h3>
          <span className="text-[11px] uppercase tracking-wide text-slate-400">
            Last {incidents.length || 0} events
          </span>
        </div>

        {incidentsLoading ? (
          <div className="py-6 text-center text-sm text-slate-400">
            <Loader2 className="w-5 h-5 animate-spin mx-auto mb-2" />
            Loading incidents…
          </div>
        ) : incidentsError ? (
          <div className="rounded-md border border-amber-500/20 bg-amber-500/10 px-3 py-2 text-xs text-amber-200">
            {incidentsError}
          </div>
        ) : incidents.length === 0 ? (
          <div className="py-6 text-center text-sm text-slate-400">
            No incidents recorded — this monitor has been steady.
          </div>
        ) : (
          <ol className="relative border-l border-white/10 pl-4 space-y-4">
            {incidents.map((inc) => {
              const label = incidentLabel(inc.type);
              const tn = kindTone(label.kind);
              return (
                <li key={inc.id} className="relative">
                  <span
                    className={`absolute -left-[21px] top-1.5 inline-block h-3 w-3 rounded-full border-2 border-slate-900 ${tn.dot}`}
                    aria-hidden
                  />
                  <div className="flex flex-wrap items-center gap-2">
                    <span
                      className={`rounded-full px-2 py-0.5 text-[10px] uppercase tracking-wide ${tn.pill}`}
                    >
                      {label.text}
                    </span>
                    <span className="text-xs text-slate-300">
                      {formatDateTime(inc.at)}
                    </span>
                    {inc.duration !== null && (
                      <span className="text-xs text-slate-400">
                        · duration {formatDuration(inc.duration)}
                      </span>
                    )}
                  </div>
                  {inc.reason && (
                    <p className="mt-1 text-xs text-slate-400">{inc.reason}</p>
                  )}
                </li>
              );
            })}
          </ol>
        )}
      </div>
    </div>
  );
}

function StatCard({ label, value }: { label: string; value: string }) {
  return (
    <div className="rounded-lg border border-white/10 bg-white/5 px-3 py-2.5">
      <div className="text-[11px] uppercase tracking-wide text-slate-400">
        {label}
      </div>
      <div className="text-lg font-semibold text-white mt-0.5">{value}</div>
    </div>
  );
}

function MetaRow({ label, value }: { label: string; value: string | null }) {
  return (
    <div className="flex items-baseline justify-between gap-3 border-b border-white/5 py-1.5">
      <span className="text-xs uppercase tracking-wide text-slate-400">{label}</span>
      <span className="text-sm text-slate-100 truncate max-w-[60%] text-right">
        {value || "—"}
      </span>
    </div>
  );
}

interface NotificationToggleProps {
  pref: UptimeNotificationPref | null;
  saving: boolean;
  error: string | null;
  onToggle: () => void;
}

/**
 * Email-on-status-change toggle. Rendered above the summary strip on
 * /client/uptime so users can enable or disable outage/recovery emails
 * for every monitor they own with a single click.
 */
function NotificationToggle({
  pref,
  saving,
  error,
  onToggle,
}: NotificationToggleProps) {
  const enabled = pref?.email_notifications_enabled ?? false;
  const email = pref?.notify_email ?? null;
  const Icon = enabled ? BellRing : BellOff;

  return (
    <section
      className={`mb-5 rounded-xl border px-4 py-4 md:px-5 md:py-4 flex flex-col md:flex-row md:items-center gap-3 md:gap-4 ${
        enabled
          ? "border-emerald-500/20 bg-emerald-500/5"
          : "border-white/10 bg-slate-900/40"
      }`}
    >
      <div className="flex items-start gap-3 flex-1 min-w-0">
        <span
          className={`mt-0.5 inline-flex h-9 w-9 shrink-0 items-center justify-center rounded-lg ${
            enabled
              ? "bg-emerald-500/15 text-emerald-300"
              : "bg-white/5 text-slate-300"
          }`}
          aria-hidden
        >
          <Icon className="h-4 w-4" />
        </span>
        <div className="min-w-0">
          <div className="flex items-center gap-2 flex-wrap">
            <h3 className="text-sm font-semibold text-white">
              Email alerts on status changes
            </h3>
            <span
              className={`rounded-full px-2 py-0.5 text-[10px] uppercase tracking-wide ${
                enabled
                  ? "bg-emerald-500/20 text-emerald-200"
                  : "bg-white/10 text-slate-300"
              }`}
            >
              {enabled ? "On" : "Off"}
            </span>
          </div>
          <p className="mt-1 text-xs text-slate-400">
            {enabled
              ? "We'll email you whenever a monitor goes down or recovers."
              : "Turn this on to get an email whenever a monitor goes down or recovers."}
          </p>
          {email && (
            <p className="mt-1 inline-flex items-center gap-1.5 text-xs text-slate-300">
              <Mail className="h-3 w-3 text-slate-400" />
              <span className="truncate">{email}</span>
            </p>
          )}
          {error && (
            <p className="mt-1.5 text-xs text-rose-300">{error}</p>
          )}
        </div>
      </div>

      {/* Switch */}
      <button
        type="button"
        role="switch"
        aria-checked={enabled}
        aria-label="Toggle email alerts on status changes"
        onClick={onToggle}
        disabled={saving || pref === null}
        className={`relative inline-flex h-6 w-11 shrink-0 items-center rounded-full transition focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-slate-950 focus:ring-emerald-500 disabled:opacity-60 disabled:cursor-not-allowed ${
          enabled ? "bg-emerald-500" : "bg-white/15"
        }`}
      >
        <span
          className={`inline-block h-5 w-5 transform rounded-full bg-white shadow transition ${
            enabled ? "translate-x-5" : "translate-x-0.5"
          }`}
        />
        {saving && (
          <Loader2 className="absolute inset-0 m-auto h-3 w-3 animate-spin text-white" />
        )}
      </button>
    </section>
  );
}

interface PackagePickerProps {
  packages: UptimePackage[];
  loading: boolean;
  activatingPackageId: number | null;
  onActivate: (packageId: number) => void;
}

/**
 * Self-service package picker shown on /client/uptime when the user has
 * no active uptime subscription. Lets clients activate a plan directly
 * without going through WHMCS billing.
 */
function PackagePicker({
  packages,
  loading,
  activatingPackageId,
  onActivate,
}: PackagePickerProps) {
  return (
    <section className="mb-6 rounded-2xl border border-emerald-500/20 bg-gradient-to-br from-emerald-500/5 via-slate-900/40 to-slate-900/40 p-5 md:p-6">
      <div className="flex items-start gap-3 mb-4">
        <span
          className="inline-flex h-10 w-10 shrink-0 items-center justify-center rounded-xl bg-emerald-500/15 text-emerald-300"
          aria-hidden
        >
          <Rocket className="h-5 w-5" />
        </span>
        <div className="min-w-0">
          <h2 className="text-base md:text-lg font-semibold text-white">
            Start monitoring your sites
          </h2>
          <p className="mt-1 text-xs md:text-sm text-slate-300">
            Pick a plan to activate your uptime subscription. You'll be able to
            add monitors, alert contacts, and status pages right away.
          </p>
        </div>
      </div>

      {loading ? (
        <div className="py-6 text-center text-sm text-slate-400">
          <Loader2 className="w-5 h-5 animate-spin mx-auto mb-2" />
          Loading plans…
        </div>
      ) : packages.length === 0 ? (
        <div className="rounded-lg border border-white/10 bg-white/5 px-4 py-6 text-center text-sm text-slate-400">
          No plans are available right now. Please contact support.
        </div>
      ) : (
        <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-3">
          {packages.map((pkg) => {
            const activating = activatingPackageId === pkg.id;
            const disabled = activatingPackageId !== null;
            return (
              <div
                key={pkg.id}
                className="rounded-xl border border-white/10 bg-slate-900/60 p-4 flex flex-col"
              >
                <div className="flex items-start justify-between gap-2">
                  <h3 className="text-sm font-semibold text-white">
                    {pkg.name}
                  </h3>
                  {pkg.price_per_month && (
                    <span className="inline-flex items-center gap-1 rounded-full bg-emerald-500/15 text-emerald-200 px-2 py-0.5 text-[11px] font-medium">
                      <Sparkles className="h-3 w-3" />
                      {pkg.price_per_month}/mo
                    </span>
                  )}
                </div>
                {pkg.description && (
                  <p className="mt-1 text-xs text-slate-400 line-clamp-2">
                    {pkg.description}
                  </p>
                )}
                <ul className="mt-3 space-y-1 text-xs text-slate-300 flex-1">
                  <li className="flex items-center gap-1.5">
                    <CheckCircle2 className="h-3.5 w-3.5 text-emerald-400" />
                    {pkg.monitors_unlimited
                      ? "Unlimited monitors"
                      : `${pkg.max_monitors} monitors`}
                  </li>
                  <li className="flex items-center gap-1.5">
                    <CheckCircle2 className="h-3.5 w-3.5 text-emerald-400" />
                    {pkg.alert_contacts_unlimited
                      ? "Unlimited alert contacts"
                      : `${pkg.max_alert_contacts} alert contacts`}
                  </li>
                  <li className="flex items-center gap-1.5">
                    <CheckCircle2 className="h-3.5 w-3.5 text-emerald-400" />
                    Check interval from {pkg.min_check_interval}s
                  </li>
                  {pkg.public_status_page_allowed && (
                    <li className="flex items-center gap-1.5">
                      <CheckCircle2 className="h-3.5 w-3.5 text-emerald-400" />
                      Public status pages
                    </li>
                  )}
                </ul>
                <button
                  type="button"
                  onClick={() => onActivate(pkg.id)}
                  disabled={disabled}
                  className="mt-4 inline-flex items-center justify-center gap-2 rounded-lg bg-emerald-500 px-3 py-2 text-sm font-semibold text-white hover:bg-emerald-400 transition disabled:opacity-60 disabled:cursor-not-allowed"
                >
                  {activating ? (
                    <Loader2 className="h-4 w-4 animate-spin" />
                  ) : (
                    <Rocket className="h-4 w-4" />
                  )}
                  {activating ? "Activating…" : "Activate plan"}
                </button>
              </div>
            );
          })}
        </div>
      )}
    </section>
  );
}