import { createFileRoute, useNavigate } from "@tanstack/react-router";
import { memo, useMemo, useState } from "react";
import { toast } from "sonner";
import {
  TrendingUp, TrendingDown, Minus, Timer, ShieldAlert, X, Layers, DollarSign,
  Activity, Wallet, Star, LineChart as LineChartIcon, AlertOctagon,
} from "lucide-react";
import { Card } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Checkbox } from "@/components/ui/checkbox";
import {
  AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent,
  AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle,
  AlertDialogTrigger,
} from "@/components/ui/alert-dialog";
import { fmtUsd, fmtNum, fmtCompact } from "@/lib/mock-data";
import { useDeltaTickers, type TickerState } from "@/hooks/use-delta-tickers";
import { useAlertsEngine } from "@/lib/alert-rules";
import { useBackendStream } from "@/hooks/use-backend-stream";
import { useConnectionToasts } from "@/hooks/use-connection-toasts";
import { ConnectionPill } from "@/components/connection-status";
import { FeedHealth, TickLatency, fmtSpreadBps } from "@/components/feed-health";

import { PendingApprovals } from "@/components/pending-approvals";
import { backendApi, type DeltaPosition, type OrderStatus } from "@/lib/backend-client";
import { useTrendingTickers } from "@/hooks/use-trending-tickers";
import { useTrendMatrixPrefs, openChartForSymbol } from "@/lib/trend-matrix-prefs";
import { useClientSignals } from "@/hooks/use-client-signals";

function DashboardError({ error, reset }: { error: Error; reset: () => void }) {
  if (typeof console !== "undefined") console.error("Dashboard crashed:", error);
  return (
    <div className="mx-auto max-w-xl p-8 text-center">
      <h1 className="text-xl font-semibold text-foreground">Dashboard failed to load</h1>
      <p className="mt-2 text-sm text-muted-foreground break-words">{error?.message ?? "Unknown error"}</p>
      <div className="mt-4 flex justify-center gap-2">
        <button
          type="button"
          onClick={() => { try { reset(); } catch { /* ignore */ } if (typeof window !== "undefined") window.location.reload(); }}
          className="rounded-md border border-primary/40 bg-primary/10 px-3 py-1.5 text-sm text-primary hover:bg-primary/20"
        >
          Reload page
        </button>
      </div>
    </div>
  );
}

export const Route = createFileRoute("/_app/dashboard")({
  head: () => ({
    meta: [
      { title: "Live Terminal · Delta Exchange" },
      { name: "description", content: "Realtime multi-coin trend matrix, active positions, and one-tap manual close." },
    ],
  }),
  component: Dashboard,
  errorComponent: DashboardError,
});

function trendOf(change24h?: number): "BULLISH" | "BEARISH" | "SIDEWAYS" {
  if (change24h == null) return "SIDEWAYS";
  if (change24h > 0.4) return "BULLISH";
  if (change24h < -0.4) return "BEARISH";
  return "SIDEWAYS";
}

function TrendPill({ trend }: { trend: "BULLISH" | "BEARISH" | "SIDEWAYS" }) {
  const cfg = {
    BULLISH: { icon: TrendingUp, cls: "bg-bull-soft text-bull" },
    BEARISH: { icon: TrendingDown, cls: "bg-bear-soft text-bear" },
    SIDEWAYS: { icon: Minus, cls: "bg-muted text-muted-foreground" },
  }[trend];
  const Icon = cfg.icon;
  return (
    <span className={`inline-flex items-center gap-1 rounded-md px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wider ${cfg.cls}`}>
      <Icon className="h-3 w-3" /> {trend}
    </span>
  );
}

const CoinCard = memo(function CoinCard({
  symbol,
  t,
  isFavorite,
  onToggleFavorite,
  onTrade,
}: {
  symbol: string;
  t?: TickerState;
  isFavorite: boolean;
  onToggleFavorite: () => void;
  onTrade: () => void;
}) {
  const trend = trendOf(t?.change24h);
  const price = t?.price;
  const glow = trend === "BULLISH" ? "hover:card-glow-bull" : trend === "BEARISH" ? "hover:card-glow-bear" : "hover:card-glow-primary";
  const changeLabel = t?.change24h != null ? `${t.change24h > 0 ? "+" : ""}${t.change24h.toFixed(2)}%` : "—";
  const priceLabel = price != null ? fmtNum(price, price < 1 ? 4 : 2) : "no data";
  return (
    <Card
      role="group"
      aria-label={`${symbol} · ${trend.toLowerCase()} · price ${priceLabel} · 24 hour change ${changeLabel}`}
      className={`glass group relative flex min-h-[11rem] flex-col overflow-hidden p-4 outline-none transition duration-300 hover:-translate-y-0.5 hover:border-primary/40 focus-within:ring-2 focus-within:ring-primary/60 ${glow}`}
    >
      <button
        type="button"
        onClick={onToggleFavorite}
        aria-pressed={isFavorite}
        aria-label={isFavorite ? `Unpin ${symbol}` : `Pin ${symbol}`}
        className={`absolute right-2 top-2 grid h-7 w-7 place-items-center rounded-md transition ${
          isFavorite ? "text-warning" : "text-muted-foreground hover:text-warning"
        }`}
      >
        <Star className={`h-4 w-4 ${isFavorite ? "fill-current" : ""}`} />
      </button>
      <div className="flex items-start justify-between gap-2 pr-8">
        <div className="min-w-0">
          <div className="flex items-center gap-1.5">
            <div className="truncate font-display text-sm font-bold tracking-wide">{symbol}</div>
            {t?.stale && (
              <span
                className="rounded-sm border border-warning/40 bg-warning/10 px-1 py-px text-[9px] font-bold uppercase tracking-wider text-warning"
                title="No ticks received in the last 10 seconds"
              >
                stale
              </span>
            )}
          </div>
          <div
            className={`num mt-1 text-2xl font-bold ${t?.flash === "up" ? "flash-up" : t?.flash === "down" ? "flash-down" : ""} ${t?.stale ? "opacity-50" : ""}`}
            title={
              t?.bid != null && t?.ask != null
                ? `Mid of best bid ${fmtNum(t.bid, t.bid < 1 ? 4 : 2)} / ask ${fmtNum(t.ask, t.ask < 1 ? 4 : 2)}`
                : t?.source === "mark"
                  ? "Delta mark price"
                  : "Last trade price"
            }
          >
            {price != null ? fmtNum(price, price < 1 ? 4 : 2) : "—"}
          </div>
          {t?.bid != null && t?.ask != null && (
            <div className="mt-1 flex items-center gap-2 text-[10px] text-muted-foreground">
              <span className="num">
                <span className="text-bull">B</span> {fmtNum(t.bid, t.bid < 1 ? 4 : 2)}
              </span>
              <span className="num">
                <span className="text-bear">A</span> {fmtNum(t.ask, t.ask < 1 ? 4 : 2)}
              </span>
              <span
                className={`num ${
                  (t.spread_bps ?? 0) > 20 ? "text-warning" : "text-muted-foreground"
                }`}
                title="Bid/ask spread in basis points"
              >
                Δ {fmtSpreadBps(t.spread_bps)}
              </span>
            </div>
          )}
        </div>
        <TrendPill trend={trend} />
      </div>
      <div className="mt-3 grid grid-cols-3 gap-2 text-[11px]">
        <MicroStat label="24h" value={changeLabel} tone={(t?.change24h ?? 0) >= 0 ? "bull" : "bear"} />
        <MicroStat label="Vol" value={t?.volume24h ? fmtCompact(t.volume24h) : "—"} tone="neutral" />
        <div className="rounded-md border border-border/60 bg-secondary/20 px-2 py-1.5">
          <div className="text-[9px] uppercase tracking-wider text-muted-foreground">Tick</div>
          <div className="mt-0.5 text-[11px] font-semibold">
            <TickLatency lastUpdate={t?.lastUpdate} />
          </div>
        </div>
      </div>

      <div className="mt-auto pt-3">
        <Button
          size="sm"
          onClick={onTrade}
          aria-label={`Take a trade on ${symbol}`}
          className="w-full gap-1.5"
        >
          <LineChartIcon className="h-3.5 w-3.5" />
          Take Trade
        </Button>
      </div>
    </Card>
  );
});

function Dashboard() {
  const navigate = useNavigate();
  const { favorites, coinCount, toggleFavorite } = useTrendMatrixPrefs();
  const { rows: trending, loading: trendingLoading, error: trendingError } = useTrendingTickers();

  // Compose watchlist: favorites (in user order) first, then top-N trending
  // symbols that aren't already favourited.
  const watchlist = useMemo(() => {
    const favSet = new Set(favorites);
    const trendingSymbols = trending.map((r) => r.symbol);
    const availableFavs = favorites.filter((s) => trendingSymbols.includes(s) || s.length > 0);
    const nonFav = trendingSymbols.filter((s) => !favSet.has(s));
    const take = Math.max(0, coinCount - availableFavs.length);
    return [...availableFavs, ...nonFav.slice(0, take)];
  }, [favorites, trending, coinCount]);

  const seed = useMemo(() => {
    const m: Record<string, TickerState> = {};
    for (const r of trending) {
      m[r.symbol] = {
        symbol: r.symbol,
        price: r.price,
        change24h: r.change24h,
        volume24h: r.volume24h,
        lastUpdate: Date.now(),
      };
    }
    return m;
  }, [trending]);

  const {
    positions,
    setPositions,
    signals,
    setSignals,
    account,
    connection: backendConn,
  } = useBackendStream();

  // Subscribe to trending watchlist AND every currently-open position symbol
  // so PnL / mark keeps ticking even for coins outside the trend matrix.
  const subscribeSymbols = useMemo(() => {
    const set = new Set<string>(watchlist);
    for (const p of positions) if (p?.coin_symbol) set.add(p.coin_symbol);
    return Array.from(set);
  }, [watchlist, positions]);

  const { tickers: liveTickers, connection: deltaConn } = useDeltaTickers(subscribeSymbols);
  useAlertsEngine(liveTickers);
  // Prefer live WS ticker for a symbol; fall back to REST trending snapshot.
  const tickers = useMemo(() => {
    const merged: Record<string, TickerState> = { ...seed };
    for (const s of subscribeSymbols) if (liveTickers[s]) merged[s] = liveTickers[s];
    return merged;
  }, [seed, liveTickers, subscribeSymbols]);

  useConnectionToasts("Delta feed", deltaConn);
  useConnectionToasts("Backend", backendConn);

  // Merge live marks from Delta into positions for real-time PnL. Fall back
  // to mark_price / last_price if the mid isn't populated yet so the row
  // updates as soon as any price signal arrives.
  const livePositions = useMemo(() => {
    return positions.map((p) => {
      const anyP = p as unknown as Record<string, unknown>;
      const t = tickers[p.coin_symbol];
      const px = t
        ? [t.price, t.mark_price, t.last_price].find((v) => Number.isFinite(v))
        : undefined;
      if (!Number.isFinite(px)) return p;
      const dir = p.position_side === "LONG" ? 1 : -1;
      const entry = Number(p.entry_price) || 0;
      const leverage = Number(p.leverage) || 1;
      const size =
        Number(p.size ?? anyP.order_size_value ?? anyP.filled_size ?? 0) || 0;
      const pnl_pct = entry ? ((px! - entry) / entry) * 100 * dir * leverage : 0;
      const pnl = (pnl_pct / 100) * size;
      const opened_at =
        p.opened_at ?? (anyP.timestamp as string | undefined) ?? p.opened_at;
      return { ...p, opened_at, current_price: px!, pnl, pnl_pct };
    });
  }, [positions, tickers]);

  const livePnl = livePositions.reduce((s, t) => s + (Number.isFinite(t.pnl) ? t.pnl : 0), 0);
  const dayPnl = useMemo(() => {
    const startOfDay = new Date();
    startOfDay.setHours(0, 0, 0, 0);
    const cutoff = startOfDay.getTime();
    return livePositions.reduce((s, t) => {
      const opened = t.opened_at ? new Date(t.opened_at).getTime() : NaN;
      if (Number.isFinite(opened) && opened >= cutoff && Number.isFinite(t.pnl)) return s + t.pnl;
      return s;
    }, 0);
  }, [livePositions]);

  // Multi-select for bulk close.
  const [selected, setSelected] = useState<Set<string>>(new Set());
  const [bulkBusy, setBulkBusy] = useState(false);
  const toggleSelected = (id: string) => {
    setSelected((prev) => {
      const next = new Set(prev);
      if (next.has(id)) next.delete(id);
      else next.add(id);
      return next;
    });
  };
  // Filters + sorting for the active-trades table.
  type SortKey = "none" | "pnl_desc" | "pnl_asc" | "day_desc" | "day_asc";
  const [coinFilter, setCoinFilter] = useState<string>("all");
  const [algoFilter, setAlgoFilter] = useState<string>("all");
  const [sortBy, setSortBy] = useState<SortKey>("none");

  const coinOptions = useMemo(
    () => Array.from(new Set(livePositions.map((p) => p.coin_symbol).filter(Boolean))).sort(),
    [livePositions],
  );
  const algoOptions = useMemo(
    () =>
      Array.from(
        new Set(
          livePositions
            .map((p) => (p.algo_name || p.algo || "").trim())
            .filter((s) => s.length > 0),
        ),
      ).sort(),
    [livePositions],
  );

  const startOfDayMs = useMemo(() => {
    const d = new Date();
    d.setHours(0, 0, 0, 0);
    return d.getTime();
  }, []);
  const dayPnlOf = (p: DeltaPosition) => {
    const opened = p.opened_at ? Date.parse(p.opened_at) : NaN;
    return Number.isFinite(opened) && opened >= startOfDayMs && Number.isFinite(p.pnl)
      ? (p.pnl as number)
      : 0;
  };

  const visiblePositions = useMemo(() => {
    let arr = livePositions;
    if (coinFilter !== "all") arr = arr.filter((p) => p.coin_symbol === coinFilter);
    if (algoFilter !== "all") {
      arr = arr.filter((p) => (p.algo_name || p.algo || "").trim() === algoFilter);
    }
    if (sortBy !== "none") {
      arr = [...arr].sort((a, b) => {
        if (sortBy === "pnl_desc" || sortBy === "pnl_asc") {
          const av = Number.isFinite(a.pnl) ? (a.pnl as number) : 0;
          const bv = Number.isFinite(b.pnl) ? (b.pnl as number) : 0;
          return sortBy === "pnl_desc" ? bv - av : av - bv;
        }
        const av = dayPnlOf(a);
        const bv = dayPnlOf(b);
        return sortBy === "day_desc" ? bv - av : av - bv;
      });
    }
    return arr;
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [livePositions, coinFilter, algoFilter, sortBy, startOfDayMs]);

  const filtersActive = coinFilter !== "all" || algoFilter !== "all";
  const allIds = visiblePositions.map((p) => p._id);
  const everyId = livePositions.map((p) => p._id);
  const allSelected = allIds.length > 0 && allIds.every((id) => selected.has(id));
  const someSelected = allIds.some((id) => selected.has(id));
  const toggleSelectAll = () => {
    setSelected((prev) => {
      if (allSelected) {
        const next = new Set(prev);
        allIds.forEach((id) => next.delete(id));
        return next;
      }
      const next = new Set(prev);
      allIds.forEach((id) => next.add(id));
      return next;
    });
  };

  async function closeMany(ids: string[]) {
    if (!ids.length) return;
    setBulkBusy(true);
    const results = await Promise.allSettled(ids.map((id) => backendApi.closePosition(id)));
    const ok = results.filter((r) => r.status === "fulfilled").length;
    const fail = results.length - ok;
    // Optimistically drop successfully-closed rows; keep failures visible.
    setPositions((prev) => prev.filter((x) => {
      const idx = ids.indexOf(x._id);
      if (idx === -1) return true;
      return results[idx]?.status !== "fulfilled";
    }));
    setSelected((prev) => {
      const next = new Set(prev);
      ids.forEach((id) => next.delete(id));
      return next;
    });
    setBulkBusy(false);
    if (ok) toast.success(`Closed ${ok} position${ok === 1 ? "" : "s"}`);
    if (fail) toast.error(`${fail} close request${fail === 1 ? "" : "s"} failed`);
  }

  async function closeTrade(t: DeltaPosition) {
    try {
      await backendApi.closePosition(t._id);
      setPositions((prev) => prev.filter((x) => x._id !== t._id));
      toast.success(`Close submitted · ${t.coin_symbol}`, {
        description: `Market close at ${fmtNum(t.current_price)}`,
      });
    } catch (e) {
      toast.error("Close failed", { description: String(e instanceof Error ? e.message : e) });
    }
  }

  function handleTakeTrade(symbol: string) {
    openChartForSymbol(symbol);
    navigate({ to: "/chart" });
    toast.info(`Opening chart · ${symbol}`, { description: "Configure size, SL/TP, then place the trade." });
  }

  function handleToggleFav(symbol: string) {
    const wasFav = favorites.includes(symbol);
    toggleFavorite(symbol);
    toast.success(wasFav ? `${symbol} unpinned` : `${symbol} pinned to top`);
  }

  return (
    <div className="space-y-6">
      {/* Connection health */}
      <div className="flex flex-wrap items-center gap-2">
        <ConnectionPill label="Delta feed" state={deltaConn.state} attempt={deltaConn.attempt} />
        <FeedHealth tickers={liveTickers} connectionState={deltaConn.state} />
        <ConnectionPill label="Backend" state={backendConn.state} attempt={backendConn.attempt} />
        {account?.connected && (
          <Badge variant="outline" className="border-primary/40 text-primary">
            Account · {account.email ?? account.account_id}
          </Badge>
        )}
      </div>


      {/* Pending signals — merge live backend feed with an in-browser
          strategy engine so the panel is never empty in preview/demos. */}
      <PendingSignalsSection
        backendSignals={signals}
        connection={backendConn}
        activeCount={livePositions.length}
        openPositions={livePositions}
        onResolved={(id) => setSignals((prev) => prev.filter((s) => s._id !== id))}
      />

      {/* Stat row */}
      <div className="grid grid-cols-2 gap-3 md:grid-cols-4">
        <Stat icon={Layers} label="Open Positions" value={String(livePositions.length)} tone="neutral" />
        <Stat icon={DollarSign} label="Unrealized PnL" value={fmtUsd(livePnl)} tone={livePnl >= 0 ? "bull" : "bear"} />
        <Stat icon={Activity} label="Coins Streaming" value={String(watchlist.length)} tone="neutral" />
        <Stat
          icon={Wallet}
          label="Account Equity"
          value={account?.balance != null ? fmtUsd(account.balance) : "—"}
          tone="neutral"
        />
      </div>

      {/* Multi-coin trend matrix */}
      <section aria-labelledby="trend-matrix-title">
        <SectionHeader
          title="Multi-Coin Trend Matrix"
          subtitle={
            trendingLoading
              ? "Fetching trending Delta perpetuals…"
              : trendingError
                ? `Delta REST unavailable · ${trendingError}`
                : `Top ${watchlist.length} by 24h turnover${favorites.length ? ` · ${favorites.length} pinned` : ""}`
          }
          titleId="trend-matrix-title"
        />
        {watchlist.length === 0 ? (
          <Card className="glass p-8 text-center text-sm text-muted-foreground">
            {trendingLoading ? "Loading trending coins…" : "No trending coins available right now."}
          </Card>
        ) : (
          <div
            role="list"
            aria-label={`${watchlist.length} coins streaming`}
            className="grid auto-rows-fr grid-cols-[repeat(auto-fill,minmax(min(100%,15rem),1fr))] gap-3"
          >
            {watchlist.map((symbol) => (
              <div role="listitem" key={symbol}>
                <CoinCard
                  symbol={symbol}
                  t={tickers[symbol]}
                  isFavorite={favorites.includes(symbol)}
                  onToggleFavorite={() => handleToggleFav(symbol)}
                  onTrade={() => handleTakeTrade(symbol)}
                />
              </div>
            ))}
          </div>
        )}
      </section>


      {/* Active trades */}
      <section>
        <div className="mb-3 flex flex-wrap items-end justify-between gap-3">
          <div className="min-w-0">
            <div className="flex flex-wrap items-center gap-2">
              <h2 className="font-display text-lg font-bold tracking-tight">Active Trades</h2>
              <span className="rounded-full border border-border bg-secondary/40 px-2 py-0.5 text-[11px] font-semibold tabular-nums">
                {livePositions.length} open
              </span>
              <span
                className={`rounded-full border px-2 py-0.5 text-[11px] font-semibold tabular-nums ${
                  livePnl >= 0
                    ? "border-emerald-500/40 bg-emerald-500/10 text-emerald-300"
                    : "border-rose-500/40 bg-rose-500/10 text-rose-300"
                }`}
                title="Unrealized PnL across all open positions"
              >
                Current PnL {livePnl >= 0 ? "+" : ""}
                {fmtNum(livePnl)}
              </span>
              <span
                className={`rounded-full border px-2 py-0.5 text-[11px] font-semibold tabular-nums ${
                  dayPnl >= 0
                    ? "border-emerald-500/40 bg-emerald-500/10 text-emerald-300"
                    : "border-rose-500/40 bg-rose-500/10 text-rose-300"
                }`}
                title="Unrealized PnL for positions opened today"
              >
                Day PnL {dayPnl >= 0 ? "+" : ""}
                {fmtNum(dayPnl)}
              </span>
            </div>
            <p className="mt-1 text-xs text-muted-foreground">
              Synced from Delta · order lifecycle live
              {someSelected && ` · ${allIds.filter((id) => selected.has(id)).length} selected`}
            </p>
          </div>
          {livePositions.length > 0 && (
            <div className="flex flex-wrap gap-2">
              <ConfirmCloseButton
                label={`Close selected${someSelected ? ` (${allIds.filter((id) => selected.has(id)).length})` : ""}`}
                disabled={!someSelected || bulkBusy}
                variant="outline"
                title="Close all selected"
                description="This will submit market-close orders for every selected position. Continue?"
                onConfirm={() => closeMany(allIds.filter((id) => selected.has(id)))}
              />
              <ConfirmCloseButton
                label={`Close filtered (${visiblePositions.length})`}
                disabled={bulkBusy || visiblePositions.length === 0 || !filtersActive}
                variant="outline"
                title="Close filtered positions"
                description="This will submit market-close orders for every position matching the current filters. Continue?"
                onConfirm={() => closeMany(allIds)}
              />
              <PanicCloseButton
                count={livePositions.length}
                disabled={bulkBusy || livePositions.length === 0}
                onConfirm={() => closeMany(everyId)}
              />
            </div>
          )}
        </div>
        {livePositions.length > 0 && (
          <div className="mb-3 flex flex-wrap items-center gap-2 text-xs">
            <label className="flex items-center gap-1.5">
              <span className="uppercase tracking-widest text-muted-foreground">Coin</span>
              <select
                value={coinFilter}
                onChange={(e) => setCoinFilter(e.target.value)}
                className="min-h-8 rounded-md border border-border bg-secondary/40 px-2 py-1 text-xs"
                aria-label="Filter by coin"
              >
                <option value="all">All ({coinOptions.length})</option>
                {coinOptions.map((c) => (
                  <option key={c} value={c}>{c}</option>
                ))}
              </select>
            </label>
            <label className="flex items-center gap-1.5">
              <span className="uppercase tracking-widest text-muted-foreground">Strategy</span>
              <select
                value={algoFilter}
                onChange={(e) => setAlgoFilter(e.target.value)}
                className="min-h-8 rounded-md border border-border bg-secondary/40 px-2 py-1 text-xs"
                aria-label="Filter by strategy"
                disabled={algoOptions.length === 0}
              >
                <option value="all">
                  {algoOptions.length === 0 ? "No strategy tags" : `All (${algoOptions.length})`}
                </option>
                {algoOptions.map((a) => (
                  <option key={a} value={a}>{a}</option>
                ))}
              </select>
            </label>
            <label className="flex items-center gap-1.5">
              <span className="uppercase tracking-widest text-muted-foreground">Sort</span>
              <select
                value={sortBy}
                onChange={(e) => setSortBy(e.target.value as typeof sortBy)}
                className="min-h-8 rounded-md border border-border bg-secondary/40 px-2 py-1 text-xs"
                aria-label="Sort positions"
              >
                <option value="none">Default</option>
                <option value="pnl_desc">Current PnL · high → low</option>
                <option value="pnl_asc">Current PnL · low → high</option>
                <option value="day_desc">Day PnL · high → low</option>
                <option value="day_asc">Day PnL · low → high</option>
              </select>
            </label>
            {(filtersActive || sortBy !== "none") && (
              <Button
                type="button"
                size="sm"
                variant="ghost"
                className="h-8 px-2 text-xs"
                onClick={() => { setCoinFilter("all"); setAlgoFilter("all"); setSortBy("none"); }}
              >
                Reset
              </Button>
            )}
            <span className="ml-auto text-muted-foreground">
              Showing {visiblePositions.length} of {livePositions.length}
            </span>
          </div>
        )}
        <Card className="glass overflow-hidden p-0">
          <div className="overflow-x-auto">
            <table className="w-full min-w-[1050px] text-sm">
              <thead className="bg-secondary/30 text-left text-[11px] uppercase tracking-wider text-muted-foreground">
                <tr>
                  <th className="px-3 py-3 w-8">
                    <Checkbox
                      checked={allSelected ? true : someSelected ? "indeterminate" : false}
                      onCheckedChange={toggleSelectAll}
                      aria-label="Select all open positions"
                    />
                  </th>
                  <th className="px-4 py-3">Symbol</th>
                  <th className="px-4 py-3">Type</th>
                  <th className="px-4 py-3">Side</th>
                  <th className="px-4 py-3">Status</th>
                  <th className="px-4 py-3">Size · Lev</th>
                  <th className="px-4 py-3">Entry / Fill</th>
                  <th className="px-4 py-3">Mark</th>
                  <th className="px-4 py-3">SL / TP</th>
                  <th className="px-4 py-3">PnL</th>
                  <th className="px-4 py-3">Wait</th>
                  <th className="px-4 py-3 text-right">Action</th>
                </tr>

              </thead>
              <tbody>
                {visiblePositions.map((t) => (
                  <TradeRow
                    key={t._id}
                    t={t}
                    selected={selected.has(t._id)}
                    onToggle={() => toggleSelected(t._id)}
                    onClose={() => closeTrade(t)}
                  />
                ))}
                {visiblePositions.length === 0 && (
                  <tr>
                    <td colSpan={12} className="px-4 py-12 text-center text-muted-foreground">
                      {livePositions.length === 0
                        ? "No open positions — approve a signal above or wait for the engine."
                        : "No positions match the current filters."}
                    </td>
                  </tr>
                )}

              </tbody>
            </table>
          </div>
        </Card>
      </section>
    </div>
  );
}

function OrderStatusBadge({ status }: { status: OrderStatus }) {
  const map: Record<OrderStatus, { label: string; cls: string }> = {
    PENDING_APPROVAL: { label: "Pending", cls: "border-warning/40 text-warning" },
    SUBMITTED: { label: "Submitted", cls: "border-primary/40 text-primary" },
    PARTIALLY_FILLED: { label: "Partial", cls: "border-warning/40 text-warning" },
    FILLED: { label: "Filled", cls: "border-bull/40 text-bull" },
    CANCELED: { label: "Canceled", cls: "border-border text-muted-foreground" },
    REJECTED: { label: "Rejected", cls: "border-bear/40 text-bear" },
  };
  const cfg = map[status] ?? map.SUBMITTED;
  return (
    <Badge variant="outline" className={cfg.cls}>
      {cfg.label}
    </Badge>
  );
}

function Stat({ label, value, tone, icon: Icon }: { label: string; value: string; tone: "bull" | "bear" | "neutral"; icon?: React.ComponentType<{ className?: string }> }) {
  const c = tone === "bull" ? "text-bull" : tone === "bear" ? "text-bear" : "text-foreground";
  const ring = tone === "bull" ? "border-bull/30" : tone === "bear" ? "border-bear/30" : "border-border";
  const accent =
    tone === "bull"
      ? "from-bull/60 via-bull/20 to-transparent"
      : tone === "bear"
        ? "from-bear/60 via-bear/20 to-transparent"
        : "from-primary/50 via-primary/10 to-transparent";
  const iconBg =
    tone === "bull"
      ? "bg-bull/10 text-bull"
      : tone === "bear"
        ? "bg-bear/10 text-bear"
        : "bg-primary/10 text-primary";
  return (
    <Card className={`glass group relative overflow-hidden p-4 transition duration-300 hover:-translate-y-0.5 hover:shadow-lg ${ring}`}>
      <span aria-hidden className={`pointer-events-none absolute inset-x-0 top-0 h-px bg-gradient-to-r ${accent}`} />
      <div className="flex items-center justify-between gap-2">
        <div className="text-[11px] uppercase tracking-widest text-muted-foreground">{label}</div>
        {Icon && (
          <div className={`grid h-8 w-8 shrink-0 place-items-center rounded-lg ${iconBg} ring-1 ring-inset ring-white/5 transition group-hover:scale-105`}>
            <Icon className="h-4 w-4" />
          </div>
        )}
      </div>
      <div className={`num mt-2 text-2xl font-bold tracking-tight ${c}`}>{value}</div>
    </Card>
  );
}

function MicroStat({ label, value, tone }: { label: string; value: string; tone: "bull" | "bear" | "neutral" }) {
  const c = tone === "bull" ? "text-bull" : tone === "bear" ? "text-bear" : "text-foreground";
  return (
    <div className="rounded-md border border-border bg-background/30 px-2 py-1.5">
      <div className="text-[9px] uppercase tracking-widest text-muted-foreground">{label}</div>
      <div className={`num text-xs font-semibold ${c}`}>{value}</div>
    </div>
  );
}

function SectionHeader({ title, subtitle, titleId }: { title: string; subtitle: string; titleId?: string }) {
  return (
    <div className="mb-3 flex items-end justify-between">
      <div>
        <h2 id={titleId} className="font-display text-lg font-bold tracking-tight">{title}</h2>
        <p className="text-xs text-muted-foreground">{subtitle}</p>
      </div>
    </div>
  );
}

function TradeRow({
  t, selected, onToggle, onClose,
}: { t: DeltaPosition; selected: boolean; onToggle: () => void; onClose: () => void }) {
  const pnl = Number.isFinite(t.pnl) ? t.pnl : 0;
  const profit = pnl >= 0;
  const openedMs = t.opened_at ? Date.parse(t.opened_at) : NaN;
  const maxWaitMs =
    Number.isFinite(t.max_wait_time_minutes) && t.max_wait_time_minutes > 0
      ? t.max_wait_time_minutes * 60_000
      : 0;
  const remaining = Number.isFinite(openedMs) && maxWaitMs > 0
    ? Math.max(0, maxWaitMs - (Date.now() - openedMs))
    : NaN;
  const waitLabel = Number.isFinite(remaining)
    ? `${Math.floor(remaining / 60_000)}:${Math.floor((remaining % 60_000) / 1000).toString().padStart(2, "0")}`
    : "—";
  const fillNote = t.filled_size != null && t.size ? ` · ${Math.round((t.filled_size / t.size) * 100)}%` : "";
  return (
    <tr className={`border-t border-border/60 transition hover:bg-secondary/20 ${selected ? "bg-primary/5" : ""}`}>
      <td className="px-3 py-3 w-8">
        <Checkbox
          checked={selected}
          onCheckedChange={onToggle}
          aria-label={`Select ${t.coin_symbol} position`}
        />
      </td>
      <td className="px-4 py-3 font-display font-bold">{t.coin_symbol}</td>
      <td className="px-4 py-3">
        <Badge
          variant="outline"
          className={
            t.execution_mode === "live"
              ? "border-bull/50 bg-bull/10 text-bull text-[10px] uppercase tracking-widest"
              : "border-primary/40 bg-primary/10 text-primary text-[10px] uppercase tracking-widest"
          }
          title={t.execution_mode === "live" ? "Routed to Delta Exchange" : "Simulated (paper) trade"}
        >
          {t.execution_mode === "live" ? "Live" : "Paper"}
        </Badge>
      </td>
      <td className="px-4 py-3">
        <Badge variant="outline" className={t.position_side === "LONG" ? "border-bull/40 text-bull" : "border-bear/40 text-bear"}>
          {t.position_side}
        </Badge>
      </td>

      <td className="px-4 py-3">
        <OrderStatusBadge status={t.order_status} />
        {fillNote && <div className="mt-0.5 text-[10px] text-muted-foreground">Filled{fillNote}</div>}
      </td>
      <td className="px-4 py-3 num text-xs">
        {t.size ? fmtUsd(t.size) : "—"} · {t.leverage ?? 1}x
      </td>
      <td className="px-4 py-3 num text-xs">
        <div>{fmtNum(t.entry_price, (t.entry_price ?? 0) < 1 ? 4 : 2)}</div>
        {t.fill_price != null && (
          <div className="text-[10px] text-muted-foreground">
            fill {fmtNum(t.fill_price, t.fill_price < 1 ? 4 : 2)}
          </div>
        )}
      </td>
      <td className="px-4 py-3 num font-semibold">
        {Number.isFinite(t.current_price) ? fmtNum(t.current_price, t.current_price < 1 ? 4 : 2) : "—"}
      </td>
      <td className="px-4 py-3 num text-xs">
        <div className="text-bear">SL {fmtNum(t.stop_loss, 2)}</div>
        <div className="text-bull">TP {fmtNum(t.take_profit, 2)}</div>
      </td>
      <td className={`px-4 py-3 num font-bold ${profit ? "text-bull" : "text-bear"}`}>
        {profit ? "+" : ""}
        {fmtUsd(pnl)}
        <div className="text-[10px] font-normal opacity-70">
          {Number.isFinite(t.pnl_pct) ? `${profit ? "+" : ""}${t.pnl_pct.toFixed(2)}%` : "—"}
        </div>
      </td>
      <td className="px-4 py-3">
        <div className="inline-flex items-center gap-1 rounded-md bg-warning/10 px-2 py-1 text-xs text-warning num">
          <Timer className="h-3 w-3" /> {waitLabel}
        </div>
      </td>
      <td className="px-4 py-3 text-right">
        <Button variant="destructive" size="sm" onClick={onClose} aria-label={`Manually close ${t.coin_symbol} position`} className="min-h-9 gap-1">
          <ShieldAlert className="h-3.5 w-3.5" aria-hidden /> Close
          <X className="h-3.5 w-3.5" aria-hidden />
        </Button>
      </td>
    </tr>
  );
}

/** Destructive confirmation dialog trigger used for bulk-close actions. */
function ConfirmCloseButton({
  label, disabled, variant, title, description, onConfirm,
}: {
  label: string;
  disabled?: boolean;
  variant: "outline" | "destructive";
  title: string;
  description: string;
  onConfirm: () => void;
}) {
  return (
    <AlertDialog>
      <AlertDialogTrigger asChild>
        <Button size="sm" variant={variant} disabled={disabled} className="gap-1">
          <ShieldAlert className="h-3.5 w-3.5" aria-hidden />
          {label}
        </Button>
      </AlertDialogTrigger>
      <AlertDialogContent>
        <AlertDialogHeader>
          <AlertDialogTitle>{title}</AlertDialogTitle>
          <AlertDialogDescription>{description}</AlertDialogDescription>
        </AlertDialogHeader>
        <AlertDialogFooter>
          <AlertDialogCancel>Cancel</AlertDialogCancel>
          <AlertDialogAction onClick={onConfirm} className="bg-bear text-primary-foreground hover:bg-bear/90">
            Confirm close
          </AlertDialogAction>
        </AlertDialogFooter>
      </AlertDialogContent>
    </AlertDialog>
  );
}

// Wraps <PendingApprovals> with the browser-side fallback engine. When the
// backend strategy service is offline we fill the panel with signals computed
// locally from live Delta candles so the operator always has something to
// react to.
function PendingSignalsSection({
  backendSignals,
  connection,
  activeCount,
  openPositions,
  onResolved,
}: {
  backendSignals: import("@/lib/backend-client").TradeSignal[];
  connection?: { state: "idle" | "connecting" | "open" | "reconnecting" | "closed"; attempt: number };
  activeCount: number;
  openPositions?: Array<{ coin_symbol: string; position_side: "LONG" | "SHORT" }>;
  onResolved: (id: string) => void;
}) {
  const { signals: clientSignals } = useClientSignals(backendSignals.length === 0);
  const [dismissed, setDismissed] = useState<Set<string>>(new Set());
  const base = backendSignals.length > 0 ? backendSignals : clientSignals;
  const merged = base.filter((s) => !dismissed.has(s._id));
  const handleResolved = (id: string) => {
    if (id.startsWith("local-")) {
      setDismissed((prev) => {
        const next = new Set(prev);
        next.add(id);
        return next;
      });
    } else {
      onResolved(id);
    }
  };
  return (
    <PendingApprovals
      signals={merged}
      onResolved={handleResolved}
      connection={connection}
      activeCount={activeCount}
      openPositions={openPositions}
    />
  );
}

/** Big red emergency panic-close button with a confirmation dialog. */
function PanicCloseButton({
  count, disabled, onConfirm,
}: { count: number; disabled?: boolean; onConfirm: () => void }) {
  return (
    <AlertDialog>
      <AlertDialogTrigger asChild>
        <Button
          size="sm"
          variant="destructive"
          disabled={disabled}
          className="gap-1 font-bold uppercase tracking-widest shadow-[0_0_0_1px_rgba(255,0,0,0.35)]"
          aria-label="Panic close all open positions"
          title="Emergency market-close every open position"
        >
          <AlertOctagon className="h-4 w-4" aria-hidden />
          Panic close all
        </Button>
      </AlertDialogTrigger>
      <AlertDialogContent>
        <AlertDialogHeader>
          <AlertDialogTitle className="flex items-center gap-2 text-bear">
            <AlertOctagon className="h-5 w-5" aria-hidden />
            PANIC CLOSE ALL
          </AlertDialogTitle>
          <AlertDialogDescription>
            This will submit <strong>market-close orders</strong> for every one of your{" "}
            <strong>{count}</strong> open position{count === 1 ? "" : "s"} — live and paper —
            immediately. This action can&apos;t be undone. Confirm only if you really want to
            flatten everything right now.
          </AlertDialogDescription>
        </AlertDialogHeader>
        <AlertDialogFooter>
          <AlertDialogCancel>Cancel</AlertDialogCancel>
          <AlertDialogAction
            onClick={onConfirm}
            className="bg-bear text-primary-foreground hover:bg-bear/90"
          >
            Yes — close everything
          </AlertDialogAction>
        </AlertDialogFooter>
      </AlertDialogContent>
    </AlertDialog>
  );
}
