import { createFileRoute } from "@tanstack/react-router";
import { memo, useCallback, useDeferredValue, useEffect, useMemo, useState } from "react";
import { keepPreviousData, useQuery } from "@tanstack/react-query";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
import { Skeleton } from "@/components/ui/skeleton";
import { Slider } from "@/components/ui/slider";
import { Checkbox } from "@/components/ui/checkbox";
import {
  backendApi,
  type AlgoStatsResponse,
  type SignalHistoryItem,
  type SignalHistoryQuery,
  type SignalHistoryResponse,
  type SignalOutcome,
} from "@/lib/backend-client";
import { useSignalsPageSize, SIGNALS_PAGE_SIZE_OPTIONS } from "@/lib/user-prefs";
import {
  ArrowDown, ArrowUp, RotateCw, Target, TrendingDown, Clock, X, CheckCircle2,
  Download, Inbox, Trophy,
} from "lucide-react";
import { toast } from "sonner";

export const Route = createFileRoute("/_app/signals")({
  head: () => ({
    meta: [
      { title: "Signals History — Delta Terminal" },
      { name: "description", content: "Trade signal history with multi-algorithm comparison, outcome tracking, filters, pagination and hit-rate stats." },
    ],
  }),
  component: SignalsHistoryPage,
});

const ALGO_OPTIONS: { id: string; name: string }[] = [
  { id: "confluence",    name: "Confluence" },
  { id: "ema_cross",     name: "EMA Cross" },
  { id: "rsi_reversal",  name: "RSI Reversal" },
  { id: "macd_momentum", name: "MACD Momentum" },
  { id: "bb_reversion",  name: "BB Reversion" },
  { id: "stochastic",    name: "Stochastic" },
];

const OUTCOMES: { value: SignalOutcome | ""; label: string }[] = [
  { value: "", label: "All outcomes" },
  { value: "target_hit", label: "Target hit" },
  { value: "stop_hit", label: "Stop hit" },
  { value: "expired", label: "Expired" },
  { value: "approved", label: "Approved" },
  { value: "pending", label: "Pending" },
  { value: "rejected", label: "Rejected" },
];

const DEFAULT_QUERY: SignalHistoryQuery = {
  q: "", side: "", outcome: "", from: "", to: "",
  algo: "", minConfidence: 0,
  sort: "created_at", dir: "desc", page: 1, limit: 10,
};

const OUTCOME_META: Record<SignalOutcome, { label: string; cls: string; Icon: typeof Target }> = {
  target_hit: { label: "Target hit", cls: "bg-emerald-500/15 text-emerald-400 border-emerald-500/30", Icon: Target },
  stop_hit:   { label: "Stop hit",   cls: "bg-red-500/15 text-red-400 border-red-500/30",             Icon: TrendingDown },
  expired:    { label: "Expired",    cls: "bg-zinc-500/15 text-zinc-300 border-zinc-500/30",          Icon: Clock },
  rejected:   { label: "Rejected",   cls: "bg-orange-500/15 text-orange-300 border-orange-500/30",    Icon: X },
  approved:   { label: "Approved",   cls: "bg-sky-500/15 text-sky-300 border-sky-500/30",             Icon: CheckCircle2 },
  pending:    { label: "Pending",    cls: "bg-amber-500/15 text-amber-300 border-amber-500/30",       Icon: Clock },
};

const OutcomeBadge = memo(function OutcomeBadge({ outcome }: { outcome: SignalOutcome }) {
  const m = OUTCOME_META[outcome];
  const Icon = m.Icon;
  return (
    <span
      role="status"
      aria-label={m.label}
      className={`inline-flex items-center gap-1 rounded-full border px-2 py-0.5 text-xs font-medium ${m.cls}`}
    >
      <Icon className="h-3 w-3" aria-hidden />
      {m.label}
    </span>
  );
});

const StatCard = memo(function StatCard({
  label, value, hint, tone = "default",
}: { label: string; value: string; hint?: string; tone?: "default" | "good" | "bad" }) {
  const toneCls = tone === "good" ? "text-emerald-400" : tone === "bad" ? "text-red-400" : "text-foreground";
  return (
    <Card className="glass-panel">
      <CardContent className="p-4">
        <div className="text-[10px] uppercase tracking-widest text-muted-foreground">{label}</div>
        <div className={`mt-1 font-display text-2xl font-bold tabular-nums ${toneCls}`}>{value}</div>
        {hint && <div className="mt-1 text-xs text-muted-foreground">{hint}</div>}
      </CardContent>
    </Card>
  );
});

// Simple debounce hook — avoids a lib dependency.
function useDebounced<T>(value: T, delay = 300): T {
  const [v, setV] = useState(value);
  useEffect(() => {
    const t = setTimeout(() => setV(value), delay);
    return () => clearTimeout(t);
  }, [value, delay]);
  return v;
}

function isoDaysAgo(days: number) {
  const d = new Date();
  d.setDate(d.getDate() - days);
  return d.toISOString().slice(0, 10);
}

function csvEscape(v: unknown) {
  const s = v == null ? "" : String(v);
  return /[",\n]/.test(s) ? `"${s.replace(/"/g, '""')}"` : s;
}

function exportSignalsCsv(items: SignalHistoryItem[]) {
  const headers = ["created_at", "coin_symbol", "position_side", "entry_price", "stop_loss", "take_profit", "leverage", "confidence", "outcome"];
  const rows = items.map((s) => [
    s.created_at, s.coin_symbol, s.position_side, s.entry_price, s.stop_loss, s.take_profit,
    s.leverage, s.confidence, s.outcome,
  ].map(csvEscape).join(","));
  const csv = [headers.join(","), ...rows].join("\n");
  const blob = new Blob([csv], { type: "text/csv;charset=utf-8" });
  const url = URL.createObjectURL(blob);
  const a = document.createElement("a");
  a.href = url;
  a.download = `signals-${new Date().toISOString().slice(0, 10)}.csv`;
  a.click();
  URL.revokeObjectURL(url);
}

function SignalsHistoryPage() {
  // Local input state — debounced before hitting the query key.
  const [qInput, setQInput] = useState("");
  const qDebounced = useDebounced(qInput, 300);

  const [prefPageSize, setPrefPageSize] = useSignalsPageSize();
  const [query, setQuery] = useState<SignalHistoryQuery>({ ...DEFAULT_QUERY, limit: prefPageSize });

  // Keep query.limit in sync when the pref changes from Settings.
  useEffect(() => {
    setQuery((prev) => (prev.limit === prefPageSize ? prev : { ...prev, limit: prefPageSize, page: 1 }));
  }, [prefPageSize]);

  // Sync debounced coin input into the query (reset to page 1 on change).
  useEffect(() => {
    setQuery((prev) => (prev.q === qDebounced ? prev : { ...prev, q: qDebounced, page: 1 }));
  }, [qDebounced]);

  const queryKey = useMemo(() => ["signals-history", query] as const, [query]);

  const { data, isLoading, isFetching, error, refetch } = useQuery<SignalHistoryResponse>({
    queryKey,
    placeholderData: keepPreviousData,
    staleTime: 15_000,
    gcTime: 5 * 60_000,
    retry: 1,
    queryFn: async () => {
      try {
        return await backendApi.listSignalHistory(query);
      } catch (e) {
        toast.error("Backend unreachable — unable to load signals");
        throw e;
      }
    },
  });

  // Algo comparison — shares the "outer" filters (coin/side/date/minConfidence).
  const algoStatsKey = useMemo(
    () => ["signals-algo-stats", {
      q: query.q, side: query.side, from: query.from, to: query.to, minConfidence: query.minConfidence,
    }] as const,
    [query.q, query.side, query.from, query.to, query.minConfidence],
  );
  const { data: algoStats } = useQuery<AlgoStatsResponse>({
    queryKey: algoStatsKey,
    placeholderData: keepPreviousData,
    staleTime: 15_000,
    retry: 1,
    queryFn: () => backendApi.listAlgoStats({
      q: query.q, side: query.side, from: query.from, to: query.to, minConfidence: query.minConfidence,
    }),
  });

  const selectedAlgos = useMemo(
    () => String(query.algo || "").split(",").map((a) => a.trim()).filter(Boolean),
    [query.algo],
  );
  const toggleAlgo = useCallback((id: string) => {
    setQuery((prev) => {
      const cur = String(prev.algo || "").split(",").map((a) => a.trim()).filter(Boolean);
      const next = cur.includes(id) ? cur.filter((a) => a !== id) : [...cur, id];
      return { ...prev, algo: next.join(","), page: 1 };
    });
  }, []);

  const update = useCallback(
    (patch: Partial<SignalHistoryQuery>) =>
      setQuery((prev) => ({ ...prev, ...patch, page: patch.page ?? 1 })),
    [],
  );

  const totalPages = data ? Math.max(1, Math.ceil(data.total / data.pageSize)) : 1;
  const stats = data?.stats;
  const hitTone: "good" | "bad" | "default" =
    !stats?.resolved ? "default" : stats.hit_rate_pct >= 50 ? "good" : "bad";

  const toggleSort = useCallback(
    (key: NonNullable<SignalHistoryQuery["sort"]>) => {
      setQuery((prev) =>
        prev.sort === key
          ? { ...prev, dir: prev.dir === "asc" ? "desc" : "asc", page: 1 }
          : { ...prev, sort: key, dir: "desc", page: 1 },
      );
    },
    [],
  );

  const applyPreset = useCallback((days: number | "all") => {
    if (days === "all") update({ from: "", to: "" });
    else update({ from: isoDaysAgo(days), to: isoDaysAgo(0) });
  }, [update]);

  const clearAll = useCallback(() => {
    setQInput("");
    setQuery(DEFAULT_QUERY);
  }, []);

  // Deferred items for smoother re-renders while typing.
  const items = useDeferredValue(data?.items ?? []);

  return (
    <div className="space-y-5">
      {/* Stats */}
      <div className="grid grid-cols-2 gap-3 md:grid-cols-4 xl:grid-cols-6">
        <StatCard label="Signals" value={String(stats?.total ?? 0)} hint="matching filters" />
        <StatCard label="Hit rate" value={`${stats?.hit_rate_pct ?? 0}%`} hint={`${stats?.target_hit ?? 0}/${stats?.resolved ?? 0} resolved`} tone={hitTone} />
        <StatCard label="Wins" value={String(stats?.target_hit ?? 0)} tone="good" />
        <StatCard label="Losses" value={String(stats?.stop_hit ?? 0)} tone="bad" />
        <StatCard label="Expired" value={String(stats?.expired ?? 0)} />
        <StatCard label="Pending / Open" value={String((stats?.pending ?? 0) + (stats?.approved ?? 0))} />
      </div>

      {/* Algorithm comparison */}
      <AlgoComparisonCard
        items={algoStats?.items ?? []}
        active={selectedAlgos}
        onToggle={toggleAlgo}
      />



      {/* Filters */}
      <Card className="glass-panel">
        <CardHeader className="pb-3">
          <CardTitle className="text-sm font-semibold">Filters</CardTitle>
        </CardHeader>
        <CardContent className="grid grid-cols-1 gap-3 md:grid-cols-6">
          <div className="md:col-span-2">
            <label htmlFor="sig-coin" className="text-xs text-muted-foreground">Coin</label>
            <Input
              id="sig-coin"
              placeholder="BTC, ETH, SOL…"
              value={qInput}
              onChange={(e) => setQInput(e.target.value)}
              aria-label="Filter by coin symbol"
            />
          </div>
          <div>
            <label className="text-xs text-muted-foreground">Side</label>
            <Select value={query.side || "all"} onValueChange={(v) => update({ side: v === "all" ? "" : (v as "LONG" | "SHORT") })}>
              <SelectTrigger aria-label="Filter by side"><SelectValue /></SelectTrigger>
              <SelectContent>
                <SelectItem value="all">All</SelectItem>
                <SelectItem value="LONG">Long</SelectItem>
                <SelectItem value="SHORT">Short</SelectItem>
              </SelectContent>
            </Select>
          </div>
          <div>
            <label className="text-xs text-muted-foreground">Outcome</label>
            <Select
              value={query.outcome || "all"}
              onValueChange={(v) => update({ outcome: v === "all" ? "" : (v as SignalOutcome) })}
            >
              <SelectTrigger aria-label="Filter by outcome"><SelectValue /></SelectTrigger>
              <SelectContent>
                {OUTCOMES.map((o) => (
                  <SelectItem key={o.value || "all"} value={o.value || "all"}>
                    {o.label}
                  </SelectItem>
                ))}
              </SelectContent>
            </Select>
          </div>
          <div>
            <label htmlFor="sig-from" className="text-xs text-muted-foreground">From</label>
            <Input id="sig-from" type="date" value={query.from ?? ""} onChange={(e) => update({ from: e.target.value })} />
          </div>
          <div>
            <label htmlFor="sig-to" className="text-xs text-muted-foreground">To</label>
            <Input id="sig-to" type="date" value={query.to ?? ""} onChange={(e) => update({ to: e.target.value })} />
          </div>

          {/* Confidence threshold */}
          <div className="md:col-span-3">
            <div className="flex items-center justify-between">
              <label htmlFor="sig-conf" className="text-xs text-muted-foreground">
                Min confidence
              </label>
              <span className="text-xs tabular-nums text-foreground">{query.minConfidence ?? 0}%</span>
            </div>
            <Slider
              id="sig-conf"
              aria-label="Minimum confidence percent"
              min={0} max={100} step={5}
              value={[Number(query.minConfidence ?? 0)]}
              onValueChange={(v) => update({ minConfidence: v[0] })}
              className="mt-2"
            />
          </div>

          {/* Algorithm multi-select */}
          <div className="md:col-span-3">
            <div className="flex items-center justify-between">
              <span className="text-xs text-muted-foreground">Algorithms</span>
              {selectedAlgos.length > 0 && (
                <button type="button" className="text-xs text-primary hover:underline" onClick={() => update({ algo: "" })}>
                  clear
                </button>
              )}
            </div>
            <div className="mt-2 flex flex-wrap gap-1.5" role="group" aria-label="Filter by algorithm">
              {ALGO_OPTIONS.map((a) => {
                const active = selectedAlgos.includes(a.id);
                return (
                  <button
                    key={a.id}
                    type="button"
                    role="checkbox"
                    aria-checked={active}
                    onClick={() => toggleAlgo(a.id)}
                    className={`inline-flex items-center gap-1 rounded-full border px-2.5 py-1 text-xs transition ${
                      active
                        ? "border-primary/50 bg-primary/15 text-primary"
                        : "border-border/40 text-muted-foreground hover:text-foreground"
                    }`}
                  >
                    <Checkbox checked={active} className="h-3 w-3 pointer-events-none" tabIndex={-1} />
                    {a.name}
                  </button>
                );
              })}
            </div>
          </div>

          {/* Date presets */}
          <div className="md:col-span-6 flex flex-wrap items-center gap-1.5">
            <span className="text-xs text-muted-foreground mr-1">Quick range:</span>
            {[
              { label: "7d", days: 7 },
              { label: "30d", days: 30 },
              { label: "90d", days: 90 },
              { label: "1y", days: 365 },
            ].map((p) => (
              <Button key={p.label} variant="outline" size="sm" onClick={() => applyPreset(p.days)}>
                {p.label}
              </Button>
            ))}
            <Button variant="outline" size="sm" onClick={() => applyPreset("all")}>All time</Button>
          </div>

          <div className="md:col-span-6 flex flex-wrap items-center gap-2">
            <Select
              value={String(query.limit ?? prefPageSize)}
              onValueChange={(v) => {
                const n = Number(v) as (typeof SIGNALS_PAGE_SIZE_OPTIONS)[number];
                setPrefPageSize(n);
                update({ limit: n });
              }}
            >
              <SelectTrigger className="w-[140px]" aria-label="Rows per page"><SelectValue /></SelectTrigger>
              <SelectContent>
                {SIGNALS_PAGE_SIZE_OPTIONS.map((n) => (
                  <SelectItem key={n} value={String(n)}>{n} / page</SelectItem>
                ))}
              </SelectContent>
            </Select>
            <Button variant="outline" size="sm" onClick={() => refetch()} disabled={isFetching}>
              <RotateCw className={`mr-1 h-3.5 w-3.5 ${isFetching ? "animate-spin" : ""}`} aria-hidden />
              Refresh
            </Button>
            <Button
              variant="outline"
              size="sm"
              onClick={() => items.length && exportSignalsCsv(items)}
              disabled={!items.length}
              title="Export current page as CSV"
            >
              <Download className="mr-1 h-3.5 w-3.5" aria-hidden />
              Export CSV
            </Button>
            <Button variant="ghost" size="sm" onClick={clearAll}>Clear filters</Button>
            {error && <span className="text-xs text-red-400">{String(error)}</span>}
          </div>
        </CardContent>
      </Card>

      {/* Table */}
      <Card className="glass-panel">
        <CardHeader className="flex flex-row items-center justify-between pb-3">
          <CardTitle className="text-sm font-semibold">Records</CardTitle>
          <div className="text-xs text-muted-foreground" aria-live="polite">
            {data ? `Page ${data.page} of ${totalPages} · ${data.total} total` : "—"}
            {isFetching && <span className="ml-2 text-primary/70">updating…</span>}
          </div>
        </CardHeader>
        <CardContent className="overflow-x-auto p-0">
          <Table>
            <TableHeader className="sticky top-0 z-10 bg-card/80 backdrop-blur">
              <TableRow>
                <SortableTh label="Date" active={query.sort === "created_at"} dir={query.dir} onClick={() => toggleSort("created_at")} />
                <SortableTh label="Coin" active={query.sort === "coin_symbol"} dir={query.dir} onClick={() => toggleSort("coin_symbol")} />
                <SortableTh label="Algo" active={query.sort === "algo"} dir={query.dir} onClick={() => toggleSort("algo")} />
                <TableHead>Side</TableHead>
                <TableHead className="text-right">Entry</TableHead>
                <TableHead className="text-right">SL</TableHead>
                <TableHead className="text-right">TP</TableHead>
                <TableHead className="text-right">Lev</TableHead>
                <SortableTh label="Conf." active={query.sort === "confidence"} dir={query.dir} onClick={() => toggleSort("confidence")} />
                <SortableTh label="Outcome" active={query.sort === "outcome"} dir={query.dir} onClick={() => toggleSort("outcome")} />
              </TableRow>
            </TableHeader>
            <TableBody>
              {isLoading && !data && (
                Array.from({ length: 6 }).map((_, i) => (
                  <TableRow key={i}>
                    {Array.from({ length: 10 }).map((__, j) => (
                      <TableCell key={j}><Skeleton className="h-4 w-full" /></TableCell>
                    ))}
                  </TableRow>
                ))
              )}
              {!isLoading && data?.items.length === 0 && (
                <TableRow>
                  <TableCell colSpan={10} className="py-14 text-center">
                    <div className="mx-auto flex max-w-sm flex-col items-center gap-2 text-muted-foreground">
                      <Inbox className="h-8 w-8 opacity-60" aria-hidden />
                      <div className="text-sm">No signals match your filters.</div>
                      <Button variant="outline" size="sm" onClick={clearAll}>Reset filters</Button>
                    </div>
                  </TableCell>
                </TableRow>
              )}
              {items.map((s) => <SignalRow key={s._id} s={s} />)}
            </TableBody>
          </Table>
        </CardContent>
        {/* Pagination */}
        <div className="flex items-center justify-between border-t border-border/40 p-3">
          <div className="text-xs text-muted-foreground">
            Showing {data ? Math.min((data.page - 1) * data.pageSize + 1, data.total) : 0}
            –{data ? Math.min(data.page * data.pageSize, data.total) : 0} of {data?.total ?? 0}
          </div>
          <div className="flex items-center gap-2">
            <Button variant="outline" size="sm" disabled={!data || data.page <= 1} onClick={() => update({ page: (data?.page ?? 1) - 1 })}>
              Prev
            </Button>
            <span className="text-xs tabular-nums">{data?.page ?? 1} / {totalPages}</span>
            <Button variant="outline" size="sm" disabled={!data || data.page >= totalPages} onClick={() => update({ page: (data?.page ?? 1) + 1 })}>
              Next
            </Button>
          </div>
        </div>
      </Card>
    </div>
  );
}

const SignalRow = memo(function SignalRow({ s }: { s: SignalHistoryItem }) {
  return (
    <TableRow className="hover:bg-muted/30">
      <TableCell className="whitespace-nowrap text-xs text-muted-foreground">
        {new Date(s.created_at).toLocaleString()}
      </TableCell>
      <TableCell className="font-medium">{s.coin_symbol}</TableCell>
      <TableCell>
        <Badge variant="outline" className="border-primary/30 text-primary text-[10px]">
          {s.algo_name ?? s.algo ?? "confluence"}
        </Badge>
      </TableCell>
      <TableCell>
        <Badge variant="outline" className={s.position_side === "LONG" ? "border-emerald-500/40 text-emerald-400" : "border-red-500/40 text-red-400"}>
          {s.position_side}
        </Badge>
      </TableCell>
      <TableCell className="text-right tabular-nums">{fmt(s.entry_price)}</TableCell>
      <TableCell className="text-right tabular-nums text-red-300/80">{fmt(s.stop_loss)}</TableCell>
      <TableCell className="text-right tabular-nums text-emerald-300/80">{fmt(s.take_profit)}</TableCell>
      <TableCell className="text-right tabular-nums">{s.leverage}x</TableCell>
      <TableCell className="text-right tabular-nums">
        <span className={`inline-block rounded-full px-2 py-0.5 text-xs ${
          (s.confidence ?? 0) >= 0.75 ? "bg-emerald-500/15 text-emerald-300"
          : (s.confidence ?? 0) >= 0.6 ? "bg-amber-500/15 text-amber-300"
          : "bg-zinc-500/15 text-zinc-300"
        }`}>
          {Math.round((s.confidence ?? 0) * 100)}%
        </span>
      </TableCell>
      <TableCell><OutcomeBadge outcome={s.outcome} /></TableCell>
    </TableRow>
  );
});

function AlgoComparisonCard({ items, active, onToggle }: {
  items: AlgoStatsResponse["items"];
  active: string[];
  onToggle: (id: string) => void;
}) {
  if (!items?.length) return null;
  const best = items[0];
  return (
    <Card className="glass-panel">
      <CardHeader className="flex flex-row items-center justify-between pb-3">
        <CardTitle className="text-sm font-semibold flex items-center gap-2">
          <Trophy className="h-4 w-4 text-amber-400" aria-hidden />
          Algorithm comparison
        </CardTitle>
        <div className="text-xs text-muted-foreground">Ranked by hit-rate · click a row to filter</div>
      </CardHeader>
      <CardContent className="overflow-x-auto p-0">
        <Table>
          <TableHeader>
            <TableRow>
              <TableHead>Algorithm</TableHead>
              <TableHead className="text-right">Signals</TableHead>
              <TableHead className="text-right">Resolved</TableHead>
              <TableHead className="text-right">Wins</TableHead>
              <TableHead className="text-right">Losses</TableHead>
              <TableHead className="text-right">Avg conf.</TableHead>
              <TableHead className="text-right">Hit rate</TableHead>
              <TableHead className="w-24">Win share</TableHead>
            </TableRow>
          </TableHeader>
          <TableBody>
            {items.map((a) => {
              const isActive = active.includes(a.algo);
              const isBest = a.algo === best.algo && a.resolved > 0;
              return (
                <TableRow
                  key={a.algo}
                  onClick={() => onToggle(a.algo)}
                  className={`cursor-pointer transition hover:bg-muted/40 ${isActive ? "bg-primary/10" : ""}`}
                >
                  <TableCell className="font-medium">
                    <div className="flex items-center gap-2">
                      {isBest && <Trophy className="h-3.5 w-3.5 text-amber-400" aria-label="Best performer" />}
                      {a.algo_name}
                      {isActive && <Badge variant="outline" className="border-primary/40 text-primary text-[10px]">active</Badge>}
                    </div>
                  </TableCell>
                  <TableCell className="text-right tabular-nums">{a.total}</TableCell>
                  <TableCell className="text-right tabular-nums">{a.resolved}</TableCell>
                  <TableCell className="text-right tabular-nums text-emerald-400">{a.target_hit}</TableCell>
                  <TableCell className="text-right tabular-nums text-red-400">{a.stop_hit}</TableCell>
                  <TableCell className="text-right tabular-nums">{a.avg_confidence_pct}%</TableCell>
                  <TableCell className={`text-right tabular-nums font-semibold ${
                    a.resolved === 0 ? "text-muted-foreground"
                    : a.hit_rate_pct >= 50 ? "text-emerald-400" : "text-red-400"
                  }`}>
                    {a.resolved ? `${a.hit_rate_pct}%` : "—"}
                  </TableCell>
                  <TableCell>
                    <div className="h-2 rounded-full bg-muted/40 overflow-hidden" aria-hidden>
                      <div
                        className={`h-full ${a.hit_rate_pct >= 50 ? "bg-emerald-500" : "bg-red-500"}`}
                        style={{ width: `${Math.min(100, a.hit_rate_pct)}%` }}
                      />
                    </div>
                  </TableCell>
                </TableRow>
              );
            })}
          </TableBody>
        </Table>
      </CardContent>
    </Card>
  );
}

function SortableTh({ label, active, dir, onClick }: { label: string; active: boolean; dir?: "asc" | "desc"; onClick: () => void }) {
  return (
    <TableHead aria-sort={active ? (dir === "asc" ? "ascending" : "descending") : "none"}>
      <button
        type="button"
        onClick={onClick}
        className={`inline-flex items-center gap-1 text-xs uppercase tracking-wide ${active ? "text-primary" : "text-muted-foreground"} hover:text-foreground`}
      >
        {label}
        {active && (dir === "asc" ? <ArrowUp className="h-3 w-3" aria-hidden /> : <ArrowDown className="h-3 w-3" aria-hidden />)}
      </button>
    </TableHead>
  );
}

function fmt(n: number | undefined) {
  if (n == null || !Number.isFinite(n)) return "—";
  return n >= 1000 ? n.toLocaleString(undefined, { maximumFractionDigits: 2 }) : n.toFixed(4);
}
