import { useMemo, useState } from 'react';
import { Link } from 'react-router-dom';
import { useActivation } from '../hooks/useActivation';
import { useRankings } from '../hooks/useScores';
import { useTrades } from '../hooks/useTrades';
import { useWatchlist } from '../hooks/useWatchlist';
import { usePaperTrades } from '../hooks/usePaperTrades';
import { useTickerNames } from '../hooks/useTickers';
import { Callout } from '../components/ui/Callout';
import { Section } from '../components/ui/Section';
import { OpenTradesPanel } from '../components/dashboard/OpenTradesPanel';
import { PerfChart } from '../components/dashboard/PerfChart';
import { PriceRail, RadarChart, radarAxesFromDimensions } from '../components/charts/horizon';
import type { RadarAxis } from '../components/charts/horizon';
import { SkeletonCard, SkeletonTable } from '../components/ui/Skeleton';
import { tradePnl } from '../lib/paperTrade';
import {
  qualifiesSetup, disqualifyReason, activationSummary, primaryTargetProbability,
} from '../lib/qualification';
import type { TradeSetup } from '../lib/types';

function fmtR(value: number | null): string {
  if (value === null) return '—';
  return `${value > 0 ? '+' : ''}${value.toFixed(2)}R`;
}

function rColor(value: number | null): string {
  if (value === null) return 'text-gray-400';
  if (value > 0) return 'text-emerald-300';
  if (value < 0) return 'text-red-300';
  return 'text-gray-300';
}

function money(value: number): string {
  const sign = value >= 0 ? '+' : '−';
  return `${sign}$${Math.abs(value).toFixed(2)}`;
}

function Metric({ label, value, sub, valueClass = 'text-gray-100' }: {
  label: string;
  value: string;
  sub?: string;
  valueClass?: string;
}) {
  return (
    <div className="glass glass-hover p-5">
      <p className="section-index">{label}</p>
      <p className={`font-display mt-2 text-3xl font-semibold ${valueClass}`}>{value}</p>
      {sub && <p className="mt-1 text-xs text-gray-500">{sub}</p>}
    </div>
  );
}

function DirectionTag({ direction }: { direction: string }) {
  const isLong = direction === 'long';
  return (
    <span className={`num inline-block rounded px-1.5 py-0.5 text-[10px] font-semibold uppercase tracking-wider ${
      isLong ? 'bg-emerald-500/15 text-emerald-300' : 'bg-red-500/15 text-red-300'
    }`}>
      {direction}
    </span>
  );
}

interface RadarRow {
  setup: TradeSetup;
  rank: number;
  reason: string | null;
}

/** One radar row — compact enough for the half-width column; selecting it
 *  swaps the focus card to this setup. */
function RadarSetupRow({ setup, rank, reason, name, selected, onSelect }: RadarRow & {
  name?: string;
  selected?: boolean;
  onSelect?: () => void;
}) {
  const qualified = reason === null;
  const prob = primaryTargetProbability(setup);
  return (
    <li
      role="button"
      tabIndex={0}
      aria-pressed={selected}
      onClick={onSelect}
      onKeyDown={(e) => {
        if (e.key === 'Enter' || e.key === ' ') {
          e.preventDefault();
          onSelect?.();
        }
      }}
      className={`grid cursor-pointer grid-cols-[20px_minmax(92px,116px)_44px_1fr_auto] items-center gap-2.5 rounded-lg px-2 py-2.5 transition-colors ${
        selected ? 'bg-blue-400/[0.08]' : 'hover:bg-white/[0.03]'
      } ${qualified ? '' : 'opacity-60'}`}
      title={`gate: R:R ${setup.rr_ratio.toFixed(1)}:1${prob != null ? ` · reach probability ${Math.round(prob)}%` : ''} (screening, not an exit) · click to focus`}
    >
      <span className="num text-[11px] text-gray-500">{rank}</span>
      <span className="min-w-0">
        <span className="block font-medium text-blue-300">{setup.symbol}</span>
        {name && <span className="block truncate text-[10.5px] text-gray-500">{name}</span>}
      </span>
      <DirectionTag direction={setup.direction} />
      <span className="flex items-center gap-2">
        <span className="h-1 flex-1 overflow-hidden rounded-full bg-white/[0.07]">
          <span
            className="block h-full rounded-full"
            style={{
              width: `${Math.round(setup.momentum_percentile ?? 0)}%`,
              background: qualified ? 'var(--up)' : 'var(--ink-3)',
            }}
          />
        </span>
        <span className="num w-11 text-[10px] text-gray-500">
          {setup.momentum_percentile != null ? `${Math.round(setup.momentum_percentile)}%ile` : '—'}
        </span>
      </span>
      <span className={`max-w-[170px] text-right text-[11.5px] leading-snug ${qualified ? 'text-blue-300' : 'text-gray-500'}`}>
        {qualified
          ? `✓ ${setup.rr_ratio.toFixed(1)}:1${prob != null ? ` · ${Math.round(prob)}%` : ''}`
          : reason}
      </span>
    </li>
  );
}

function convictionLabel(action: TradeSetup['recommended_action']): string {
  if (!action || action === 'NEUTRAL') return '—';
  if (action.endsWith('_HIGH')) return 'High';
  if (action.endsWith('_MODERATE')) return 'Moderate';
  return '—';
}

/** The focal card: a setup as a spatial price rail — the top pick by default,
 *  or whichever radar row is selected. */
function FocusCard({ setup, name, badge, badgeTone, footNote, onReset }: {
  setup: TradeSetup;
  name: string | undefined;
  badge: string;
  badgeTone: 'ember' | 'muted';
  footNote: string;
  onReset?: () => void;
}) {
  const prob = primaryTargetProbability(setup);
  return (
    <section className="glass p-6 sm:p-7" aria-label={`Setup in focus: ${setup.symbol}`}>
      <div className="flex flex-wrap items-start justify-between gap-6">
        <div className="flex items-start gap-4">
          <span className={`num mt-2.5 whitespace-nowrap rounded-full border px-2.5 py-1 text-[9.5px] font-semibold uppercase tracking-[0.2em] ${
            badgeTone === 'ember'
              ? 'border-[#ff6a45]/40 text-[#ff6a45]'
              : 'border-white/[0.15] text-gray-400'
          }`}>
            {badge}
          </span>
          <div>
            <div className="flex items-baseline gap-3">
              <h2 className="font-display text-4xl font-bold tracking-tight text-gray-100 sm:text-5xl">
                {setup.symbol}
              </h2>
              {name && <span className="text-sm text-gray-400">{name}</span>}
            </div>
            <div className="mt-2.5 flex flex-wrap items-center gap-2">
              <DirectionTag direction={setup.direction} />
              <span className="rounded-full border border-white/[0.09] px-2.5 py-0.5 text-[11.5px] text-gray-400">
                conviction {convictionLabel(setup.recommended_action)}
              </span>
            </div>
          </div>
        </div>
        {/* The headline stat is the signal that actually selected this ticker.
            R:R and reach probability are gate inputs computed from a GTL
            proposal the trade never exits at — they get quiet treatment. */}
        <div className="flex items-start gap-10 text-right">
          <div title="Residual 12-1 month momentum percentile across the universe. This is why the ticker was selected.">
            <p className="section-index">residual momentum</p>
            {setup.momentum_percentile != null ? (
              <>
                <p className="font-display mt-1 text-3xl font-semibold text-gray-100">
                  top {Math.max(1, Math.round(100 - setup.momentum_percentile))}
                  <span className="text-lg text-gray-400">%</span>
                </p>
                <div className="ml-auto mt-2 h-1 w-28 rounded-full bg-blue-500/20">
                  <span
                    className="block h-full rounded-full bg-blue-500"
                    style={{ width: `${Math.min(100, Math.round(setup.momentum_percentile))}%` }}
                  />
                </div>
              </>
            ) : (
              <>
                <p className="font-display mt-1 text-2xl font-semibold text-amber-200">missing</p>
                <p className="mt-1 text-[11px] text-gray-500">no residual rank · cannot qualify</p>
              </>
            )}
          </div>
          <div
            className="max-w-[13rem]"
            title="Gate metrics. The reward/risk and reach probability of the headline Gate Target Ladder proposal are what admitted this setup. The trade does NOT exit there — it exits on the trailing stop."
          >
            <p className="section-index">gate metrics</p>
            <p className="num mt-1.5 text-sm text-gray-300">
              R:R {setup.rr_ratio.toFixed(1)}:1
              {prob != null && <> · reach {Math.round(prob)}%</>}
            </p>
            <p className="mt-1 text-[10.5px] leading-relaxed text-gray-500">
              screening only — exits on the trailing stop, not at the level
            </p>
          </div>
        </div>
      </div>

      <PriceRail
        direction={setup.direction}
        entry={setup.entry_price}
        stop={setup.stop_loss}
        target={setup.target}
        current={setup.current_price}
      />

      <div className="mt-4 flex flex-wrap items-center justify-between gap-4 border-t border-white/[0.06] pt-4">
        <span className="text-xs text-gray-500">{footNote}</span>
        <span className="flex items-center gap-2.5">
          {onReset && (
            <button
              onClick={onReset}
              className="text-xs font-medium text-gray-400 transition-colors hover:text-gray-200"
            >
              ← top pick
            </button>
          )}
          <Link
            to={`/ticker/${setup.symbol}`}
            className="rounded-lg border border-blue-500/35 bg-blue-500/15 px-4 py-1.5 text-[13px] font-semibold text-blue-300 transition-colors hover:bg-blue-500/25"
          >
            Ticker details
          </Link>
          <Link
            to="/signals"
            className="rounded-lg border border-white/[0.09] px-4 py-1.5 text-[13px] font-semibold text-gray-400 transition-colors hover:bg-white/[0.04] hover:text-gray-200"
          >
            All setups
          </Link>
        </span>
      </div>
    </section>
  );
}

export default function DashboardPage() {
  const trades = useTrades();
  const watchlist = useWatchlist();
  const tickerNames = useTickerNames();
  const activation = useActivation();
  const openTrades = usePaperTrades('open');

  const qualifiedSetups = useMemo(
    () =>
      activation.data
        ? (trades.data ?? []).filter((t) => qualifiesSetup(t, activation.data!))
        : [],
    [trades.data, activation.data],
  );

  // Rank only actionable/qualified setups by the production strategy score.
  // Residual momentum still gates qualification; strategy_rank is the promoted
  // 80/20 residual-momentum/high-vol ordering score when available.
  const topSetups: TradeSetup[] = useMemo(() => {
    return [...qualifiedSetups]
      .sort(
        (a, b) =>
          (b.strategy_rank ?? b.momentum_percentile ?? -Infinity) -
          (a.strategy_rank ?? a.momentum_percentile ?? -Infinity),
      )
      .slice(0, 5);
  }, [qualifiedSetups]);

  // Radar: every live setup ranked by the same score, with the gate verdict.
  // Qualified setups lead (that's the actionable list); the rest sit behind a
  // toggle so a 0-qualified day isn't a wall of rejections.
  const radar = useMemo(() => {
    if (!activation.data) return { qualified: [] as RadarRow[], below: [] as RadarRow[] };
    const ranked = [...(trades.data ?? [])]
      .sort(
        (a, b) =>
          (b.strategy_rank ?? b.momentum_percentile ?? -Infinity) -
          (a.strategy_rank ?? a.momentum_percentile ?? -Infinity),
      )
      .map((setup, i) => ({
        setup,
        rank: i + 1,
        reason: disqualifyReason(setup, activation.data!),
      }));
    return {
      qualified: ranked.filter((r) => r.reason === null),
      below: ranked.filter((r) => r.reason !== null).slice(0, 8),
    };
  }, [trades.data, activation.data]);

  // Mini score fingerprints for the qualified setups (dimension scores come
  // from the rankings endpoint; axis order is the canonical one shared with
  // the ticker page so shapes stay comparable everywhere).
  const rankings = useRankings();
  const fingerprints = useMemo(() => {
    const dimsBySymbol = new Map<string, RadarAxis[]>();
    for (const r of rankings.data?.rankings ?? []) {
      if (r.dimensions.length >= 3) {
        dimsBySymbol.set(r.symbol.toUpperCase(), radarAxesFromDimensions(r.dimensions));
      }
    }
    return radar.qualified
      .slice(0, 3)
      .map(({ setup, rank }) => ({ symbol: setup.symbol, rank, axes: dimsBySymbol.get(setup.symbol.toUpperCase()) }))
      .filter((f): f is { symbol: string; rank: number; axes: RadarAxis[] } => !!f.axes);
  }, [rankings.data, radar.qualified]);

  const [belowChoice, setBelowChoice] = useState<boolean | null>(null);
  // Default open when nothing qualifies — the near-misses ARE the content then.
  const showBelow = belowChoice ?? radar.qualified.length === 0;

  // Radar selection swaps the focus card; null = the top pick.
  const [focusId, setFocusId] = useState<number | null>(null);

  const topWatchlist = useMemo(
    () =>
      [...(watchlist.data ?? [])]
        .sort((a, b) => (b.composite_score ?? -1) - (a.composite_score ?? -1))
        .slice(0, 12),
    [watchlist.data],
  );

  const today = new Date().toLocaleDateString('en-US', {
    weekday: 'long', month: 'long', day: 'numeric',
  });

  // Current exposure from open paper trades: $ at risk to stops + mark-to-market.
  const exposure = useMemo(() => {
    const rows = openTrades.data ?? [];
    let riskUsd = 0, unrealUsd = 0, unrealR = 0, rPriced = 0, winners = 0, losers = 0;
    let alphaUsd = 0, alphaPriced = 0;
    for (const t of rows) {
      riskUsd += Math.abs(t.entry_price - t.stop_loss) * t.shares;
      if (t.alpha_usd != null) { alphaUsd += t.alpha_usd; alphaPriced += 1; }
      const p = tradePnl(t);
      if (!p) continue;
      unrealUsd += p.pnl;
      if (p.r != null) { unrealR += p.r; rPriced += 1; }
      if (p.pnl > 0) winners += 1;
      else if (p.pnl < 0) losers += 1;
    }
    return { count: rows.length, riskUsd, unrealUsd, unrealR, rPriced, winners, losers, alphaUsd, alphaPriced };
  }, [openTrades.data]);

  const liveCount = trades.data?.length ?? 0;
  const qualifiedCount = qualifiedSetups.length;
  const verdict = !trades.data || !activation.data
    ? 'Market overview'
    : qualifiedCount === 0
      ? 'No setup clears the gate today.'
      : qualifiedCount === 1
        ? 'One setup clears the gate today.'
        : `${qualifiedCount} setups clear the gate today.`;

  const topPick = topSetups[0];

  // What the focus card shows: the selected radar row, else the top pick.
  const focusRow = focusId != null
    ? [...radar.qualified, ...radar.below].find((r) => r.setup.id === focusId) ?? null
    : null;
  const focusSetup = focusRow?.setup ?? topPick;
  const focusIsTop = focusRow == null || (topPick != null && focusRow.setup.id === topPick.id);

  return (
    <div className="space-y-8 animate-slide-up">
      {/* Hero — the verdict */}
      <div>
        <p className="num text-xs uppercase tracking-[0.22em] text-gray-500">{today}</p>
        <h1 className="font-display mt-1 text-4xl font-semibold tracking-tight text-gray-100">
          {verdict}
        </h1>
        {trades.data && (
          <p className="mt-2 text-[15px] text-gray-400">
            {qualifiedCount} qualified of {liveCount} live setups · {exposure.count} open position{exposure.count === 1 ? '' : 's'}
            {exposure.rPriced > 0 && (
              <>
                {' · '}
                <b className={rColor(exposure.unrealR)}>{fmtR(exposure.unrealR)}</b> unrealized
              </>
            )}
          </p>
        )}
      </div>

      {/* Watchlist — a slim glanceable strip right up top (regime moved to the nav bar) */}
      {watchlist.data && (
        topWatchlist.length > 0 ? (
          <div className="flex flex-wrap items-center gap-2">
            <span className="section-index mr-1">Watchlist</span>
            {topWatchlist.map((entry) => (
              <Link
                key={entry.symbol}
                to={`/ticker/${entry.symbol}`}
                className="inline-flex items-baseline gap-2 rounded-full border border-white/[0.09] px-3 py-1 text-[12.5px] transition-colors hover:border-blue-400/40 hover:bg-white/[0.03]"
              >
                <b className="font-semibold text-gray-200">{entry.symbol}</b>
                <span className={`num text-[11px] ${rColor(entry.change_pct)}`}>
                  {entry.change_pct != null
                    ? `${entry.change_pct >= 0 ? '+' : ''}${entry.change_pct.toFixed(2)}%`
                    : '—'}
                </span>
              </Link>
            ))}
            <Link to="/market" className="ml-1 text-xs font-medium text-blue-300 transition-colors hover:text-blue-200">
              Full watchlist →
            </Link>
          </div>
        ) : (
          <p className="text-xs text-gray-600">
            <span className="section-index mr-2">Watchlist</span>
            empty — open any ticker and tap ☆ to add it.
          </p>
        )
      )}

      {/* Setup in focus — full width; select a radar row below to swap it */}
      <div>
          {(trades.isLoading || activation.isLoading) && <SkeletonCard />}
          {trades.isError && <Callout variant="error">Failed to load setups</Callout>}
          {trades.data && activation.data && (
            focusSetup ? (
              <FocusCard
                setup={focusSetup}
                name={tickerNames.get(focusSetup.symbol.toUpperCase())}
                badge={
                  focusIsTop
                    ? 'top pick'
                    : focusRow && focusRow.reason === null
                      ? `rank ${focusRow.rank}`
                      : `rank ${focusRow?.rank ?? '—'} · below gate`
                }
                badgeTone={focusIsTop ? 'ember' : 'muted'}
                footNote={
                  focusRow && focusRow.reason !== null
                    ? `does not qualify: ${focusRow.reason}`
                    : `cleared the gate · ${activationSummary(activation.data)}`
                }
                onReset={focusIsTop ? undefined : () => setFocusId(null)}
              />
            ) : (
              <Callout variant="empty">
                No qualified actionable setups right now — select a radar row to inspect what's close and why it doesn't qualify.
              </Callout>
            )
          )}
      </div>

      {/* Metric strip — the account ribbons, right above the positions they describe */}
      {(trades.isLoading || openTrades.isLoading) ? (
        <div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-4">
          <SkeletonCard /><SkeletonCard /><SkeletonCard /><SkeletonCard />
        </div>
      ) : (
        <div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-4">
          <Metric
            label="Open Risk"
            value={exposure.count > 0 ? `$${exposure.riskUsd.toFixed(0)}` : '—'}
            sub={exposure.count > 0 ? `${exposure.count} open · distance to stops` : 'no open trades'}
          />
          <Metric
            label="Unrealized"
            value={exposure.rPriced > 0 ? fmtR(exposure.unrealR) : '—'}
            valueClass={rColor(exposure.rPriced > 0 ? exposure.unrealR : null)}
            sub={
              exposure.count > 0
                ? `${money(exposure.unrealUsd)} · ${exposure.winners}▲ ${exposure.losers}▼`
                : 'mark-to-market'
            }
          />
          <Metric
            label="Alpha vs S&P 500"
            value={exposure.alphaPriced > 0 ? money(exposure.alphaUsd) : '—'}
            valueClass={
              exposure.alphaPriced > 0
                ? exposure.alphaUsd >= 0 ? 'text-emerald-300' : 'text-red-300'
                : 'text-gray-100'
            }
            sub="open trades vs buy-and-hold SPY"
          />
          <Metric
            label="Gate"
            value={trades.data ? `${qualifiedCount} / ${liveCount}` : '—'}
            valueClass={qualifiedCount > 0 ? 'text-blue-300' : 'text-gray-100'}
            sub={activation.data ? activationSummary(activation.data) : 'setups clearing the gate'}
          />
        </div>
      )}

      {/* Open positions + performance | Radar — side by side, half width each */}
      <div className="grid items-start gap-8 xl:grid-cols-2">
        <div className="space-y-8">
          <OpenTradesPanel />
          <PerfChart />
        </div>

        <div>
          <Section title="Radar" hint="ranked by strategy score · select a row to focus it">
            {trades.isLoading && <SkeletonTable rows={5} cols={5} />}
            {trades.data && radar.qualified.length === 0 && radar.below.length === 0 && (
              <Callout variant="empty">No live setups right now.</Callout>
            )}
            {(radar.qualified.length > 0 || radar.below.length > 0) && (
              <div className="glass px-4 py-2">
                {/* Qualified fingerprints — same axes, shapes compare at a glance */}
                {fingerprints.length > 0 && (
                  <div className="flex flex-wrap items-end gap-4 border-b border-white/[0.06] px-2 pb-3 pt-1.5">
                    {fingerprints.map((f) => (
                      <figure key={f.symbol} className="text-center">
                        <RadarChart axes={f.axes} size={82} labels={false} />
                        <figcaption className="-mt-1">
                          <Link to={`/ticker/${f.symbol}`} className="block text-[13px] font-semibold text-gray-200 hover:text-blue-200">
                            {f.symbol}
                          </Link>
                          <span className={`num text-[9px] uppercase tracking-[0.14em] ${
                            f.rank === 1 ? 'text-[#ff6a45]' : 'text-gray-500'
                          }`}>
                            {f.rank === 1 ? 'top pick' : `rank ${f.rank}`}
                          </span>
                        </figcaption>
                      </figure>
                    ))}
                    <p className="num mb-2 ml-auto max-w-[170px] text-right text-[10px] leading-relaxed text-gray-500">
                      qualified fingerprints · hover a corner for scores
                    </p>
                  </div>
                )}

                {/* Qualified rows — the actionable list */}
                {radar.qualified.length > 0 ? (
                  <ul className="divide-y divide-white/[0.04]">
                    {radar.qualified.map((row) => (
                      <RadarSetupRow
                        key={row.setup.id}
                        {...row}
                        name={tickerNames.get(row.setup.symbol.toUpperCase())}
                        selected={focusSetup?.id === row.setup.id}
                        onSelect={() => setFocusId(row.setup.id)}
                      />
                    ))}
                  </ul>
                ) : (
                  <p className="px-2 py-2.5 text-xs text-gray-500">
                    None clear the gate today — the closest candidates are below.
                  </p>
                )}

                {/* Below the gate, collapsed by default when there are qualified setups */}
                {radar.below.length > 0 && (
                  <>
                    <button
                      onClick={() => setBelowChoice(!showBelow)}
                      aria-expanded={showBelow}
                      className="flex w-full items-center gap-2 border-t border-white/[0.06] px-2 py-2.5 text-left text-xs text-gray-500 transition-colors hover:text-gray-300"
                    >
                      <span className={`text-[9px] transition-transform ${showBelow ? 'rotate-90' : ''}`} aria-hidden="true">▶</span>
                      {radar.below.length} below the gate — why each doesn't qualify
                    </button>
                    {showBelow && (
                      <ul className="divide-y divide-white/[0.04] border-t border-white/[0.04]">
                        {radar.below.map((row) => (
                          <RadarSetupRow
                            key={row.setup.id}
                            {...row}
                            name={tickerNames.get(row.setup.symbol.toUpperCase())}
                            selected={focusSetup?.id === row.setup.id}
                            onSelect={() => setFocusId(row.setup.id)}
                          />
                        ))}
                      </ul>
                    )}
                  </>
                )}

                <div className="flex justify-end border-t border-white/[0.04] px-2 py-2.5">
                  <Link to="/signals" className="text-xs font-medium text-blue-300 transition-colors hover:text-blue-200">
                    All setups →
                  </Link>
                </div>
              </div>
            )}
          </Section>
        </div>
      </div>
    </div>
  );
}
