"use client";

/**
 * Admin → Signal Overview
 * ───────────────────────
 * Platform-wide rollup of SiteEvent (the generic UX / intelligence
 * signal store). Headline counts by signal type + top 25 brands by
 * volume. Window selector (1d / 7d / 30d / 90d).
 *
 * Error handling: infinite-spinner bug fix. Previously the page
 * gated its render on `loading || !data` so a failing fetch (API
 * 500, timeout) sent loading → false but left data null, leaving
 * the spinner forever with no error message. Now:
 *   • an `error` state carries the failure message
 *   • a 10s AbortController prevents hangs when the API takes too
 *     long (common symptom: the SiteEvent groupBy queries scanning
 *     the full table instead of using an index)
 *   • the loading check renders an error card when error is set
 */

import { useEffect, useState } from "react";
import Link from "next/link";
import { Radio, Loader2, AlertTriangle, RefreshCw } from "lucide-react";

interface Payload {
  windowDays: number;
  totalCount: number;
  byType: Array<{ type: string; count: number }>;
  topBrands: Array<{ brandId: string; brandName: string | null; brandDomain: string | null; count: number }>;
}

const SIGNAL_LABEL: Record<string, string> = {
  rage_click: "Rage clicks",
  dead_click: "Dead clicks",
  hesitation: "Hesitation",
  exit_intent: "Exit intent",
  js_error: "JS errors",
  outbound_click: "Outbound clicks",
  form_start: "Form starts",
  search_query: "On-site searches",
  copy_event: "Copy events",
  slow_resource: "Slow resources",
  third_party_impact: "Third-party impact",
  seo_change: "SEO changes",
};

export default function AdminSignalsPage() {
  const [days, setDays] = useState(30);
  const [data, setData] = useState<Payload | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const [retryKey, setRetryKey] = useState(0);

  useEffect(() => {
    setLoading(true);
    setError(null);
    // 10-second cap so a hung API doesn't leave the page in a
    // perpetual spinner. The API itself also enforces maxDuration=30
    // server-side; the client-side 10s aborts earlier to keep the
    // UI responsive. Operators see a "Failed to load" card with a
    // Retry button instead of a frozen loader.
    const controller = new AbortController();
    const timeoutId = setTimeout(() => controller.abort(), 10_000);

    fetch(`/api/admin/signals?days=${days}`, { signal: controller.signal })
      .then(async (r) => {
        if (!r.ok) {
          // Read the JSON error body when present so the UI surfaces
          // what the API actually said (rate limit, 500, etc.).
          let msg = `HTTP ${r.status}`;
          try {
            const body = await r.json();
            if (body?.error) msg = `${msg}: ${body.error}`;
          } catch {}
          throw new Error(msg);
        }
        return r.json() as Promise<Payload>;
      })
      .then((d) => setData(d))
      .catch((err: Error) => {
        if (err.name === "AbortError") {
          setError("Request timed out after 10s — the signals API is taking longer than expected. Try a shorter window or retry.");
        } else {
          setError(err.message || "Failed to load signals.");
        }
      })
      .finally(() => {
        clearTimeout(timeoutId);
        setLoading(false);
      });

    return () => {
      clearTimeout(timeoutId);
      controller.abort();
    };
  }, [days, retryKey]);

  if (error) {
    return (
      <div className="max-w-2xl mx-auto mt-16">
        <div className="rounded-xl border border-rose-200 bg-rose-50 p-6 text-center">
          <AlertTriangle className="w-8 h-8 text-rose-500 mx-auto mb-2" />
          <h2 className="text-sm font-bold text-slate-900">Couldn&apos;t load Signal Overview</h2>
          <p className="text-xs text-slate-600 mt-1">{error}</p>
          <button
            onClick={() => setRetryKey((k) => k + 1)}
            className="inline-flex items-center gap-1.5 px-3 py-1.5 mt-4 rounded-lg bg-white border border-slate-200 text-xs font-semibold text-slate-700 hover:bg-slate-50"
          >
            <RefreshCw className="w-3 h-3" /> Retry
          </button>
        </div>
      </div>
    );
  }

  if (loading || !data) {
    return (
      <div className="flex items-center justify-center py-24">
        <Loader2 className="w-5 h-5 text-slate-400 animate-spin" />
      </div>
    );
  }

  const max = Math.max(...data.byType.map((t) => t.count), 1);

  return (
    <div className="space-y-5">
      <div className="flex items-start justify-between">
        <div>
          <h1 className="text-2xl font-bold text-slate-900 flex items-center gap-2">
            <Radio className="w-5 h-5 text-slate-700" /> Signal Overview
          </h1>
          <p className="text-slate-500 text-sm mt-1">
            {data.totalCount.toLocaleString()} signal{data.totalCount === 1 ? "" : "s"} across all brands in the last {data.windowDays} day{data.windowDays === 1 ? "" : "s"}.
          </p>
        </div>
        <select
          value={days}
          onChange={(e) => setDays(Number(e.target.value))}
          className="px-3 py-1.5 text-xs bg-white border border-slate-200 rounded-lg"
        >
          <option value={1}>Last 24 hours</option>
          <option value={7}>Last 7 days</option>
          <option value={30}>Last 30 days</option>
          <option value={90}>Last 90 days</option>
        </select>
      </div>

      {/* By type — bar list */}
      <div className="bg-white border border-slate-200 rounded-xl overflow-hidden">
        <div className="px-4 py-3 border-b border-slate-100 font-bold text-slate-800 text-sm">
          By signal type
        </div>
        {data.byType.length === 0 ? (
          <div className="p-12 text-center text-slate-400 text-sm">No signals in this window yet.</div>
        ) : (
          <ul className="divide-y divide-slate-100">
            {data.byType.map((t) => {
              const pct = (t.count / max) * 100;
              return (
                <li key={t.type} className="px-4 py-3 flex items-center gap-3">
                  <span className="text-xs font-semibold text-slate-700 min-w-[180px]">
                    {SIGNAL_LABEL[t.type] ?? t.type}
                  </span>
                  <div className="flex-1 h-2 bg-slate-100 rounded-full overflow-hidden">
                    <div className="h-full rounded-full bg-violet-500" style={{ width: `${pct}%` }} />
                  </div>
                  <span className="text-sm font-bold text-slate-800 tabular-nums w-20 text-right">
                    {t.count.toLocaleString()}
                  </span>
                </li>
              );
            })}
          </ul>
        )}
      </div>

      {/* Top brands */}
      <div className="bg-white border border-slate-200 rounded-xl overflow-hidden">
        <div className="px-4 py-3 border-b border-slate-100 font-bold text-slate-800 text-sm">
          Brands with most signals (top 25)
        </div>
        {data.topBrands.length === 0 ? (
          <div className="p-12 text-center text-slate-400 text-sm">No brand signals in this window yet.</div>
        ) : (
          <ul className="divide-y divide-slate-100">
            {data.topBrands.map((b, i) => (
              <li key={b.brandId} className="px-4 py-2.5 flex items-center gap-3">
                <span className="text-[10px] font-bold text-slate-400 w-5 text-right">{i + 1}</span>
                <Link
                  href={`/admin/brands?q=${encodeURIComponent(b.brandDomain ?? "")}`}
                  className="flex-1 text-xs font-semibold text-slate-800 hover:text-brand-600 truncate"
                >
                  {b.brandName}
                </Link>
                <span className="text-[10px] text-slate-400 truncate max-w-[180px] font-mono">{b.brandDomain}</span>
                <span className="text-sm font-bold text-slate-800 tabular-nums w-20 text-right">
                  {b.count.toLocaleString()}
                </span>
              </li>
            ))}
          </ul>
        )}
      </div>
    </div>
  );
}
