"use client";

import * as React from "react";

import { format } from "date-fns";
import { useChanged } from "@/hooks/use-changed";
import { resolveCustomerTenant } from "@/app/customer/_lib/resolve-customer-tenant";
import Link from "next/link";
import { useParams } from "next/navigation";
import { Area, AreaChart, Bar, BarChart, CartesianGrid, LabelList, XAxis, YAxis } from "recharts";
import {
  ArrowDownRight,
  ArrowUpRight,
  ChevronRight,
  Hash,
  Layers3,
  LineChart,
  Percent,
  RefreshCw,
  Repeat2,
  Sparkles,
  TrendingDown,
  TrendingUp,
  Wallet,
  X,
} from "lucide-react";
import { toast } from "sonner";

import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import {
  ChartContainer,
  ChartTooltip,
  ChartTooltipContent,
  type ChartConfig,
} from "@/components/ui/chart";
import { cn } from "@/lib/utils";
import { formatQuantity } from "@/lib/format/numbers";
import {
  getInitialAssetAnalyticsFilters,
  loadUserReportSettings,
  saveUserReportSettings,
} from "@/lib/report-user-settings";
import { fetchReportPrefsClient } from "@/app/customer/_lib/report-prefs-client";
import { tickerFromSearch } from "../../_lib/asset-analytics-url";

import { AssetAnalyticsFiltersDialog } from "./asset-analytics-filters-dialog";
import { DerivativeLedgerBrief } from "./asset-analytics-options-panel";
import {
  AssetAnalyticsPositionsSheet,
  type AssetAnalyticsPositionsTarget,
} from "./asset-analytics-positions-sheet";
import { fetchAssetAnalyticsPositionsClient } from "../../_lib/asset-analytics-positions-api";
import type {
  AssetAnalyticsPositionRow,
  AssetAnalyticsPositionsTotals,
} from "../../_lib/map-asset-analytics-positions";
import {
  ACCUMULATORS_TAB_LABEL,
  ACCUMULATORS_TAB_SLUG,
  DERIVATIVES_TAB_SLUG,
  STRUCTURE_TAB_SLUG,
  type AssetAnalyticsAssetTab,
  type AssetAnalyticsFilters,
  defaultAssetAnalyticsFilters,
  hasActiveAssetAnalyticsFilters,
} from "./schema";
import { filterTickerGroups, groupMetrics, parseGainTone, sortAssetTabs } from "./utils";
import { useAssetAnalyticsList } from "../../_lib/use-asset-analytics-list";
import { useImpersonationScopeRefresh } from "@/app/customer/_lib/admin/use-impersonation-scope-refresh";
import { useReportBankModeInvalidation } from "@/app/customer/_lib/use-report-bank-mode-invalidation";
import { catalogForDisplay, sanitizeAssetClassSelection, sameAssetClassSelection } from "@/app/customer/[tenant]/reports/_shared/lib/asset-class-filter-catalog";
import { useAssetClassFilterCatalog } from "@/app/customer/[tenant]/reports/_shared/lib/use-asset-class-filter-catalog";
import { useDefaultLayout } from "@/app/customer/_lib/use-default-layout";

const chartConfig = { price: { label: "Price", color: "hsl(142 71% 45%)" } } satisfies ChartConfig;

function formatChartDate(date: string): string {
  const parsed = new Date(`${date}T00:00:00`);
  if (Number.isNaN(parsed.getTime())) return date;
  return format(parsed, "MMM d");
}

function formatChartPrice(value: number): string {
  if (!Number.isFinite(value)) return "";
  return Intl.NumberFormat("en-US", { notation: "compact", maximumFractionDigits: 1 }).format(value);
}

/** Full-precision price for a headline figure — formatChartPrice's compact notation is for axis ticks only. */
function formatExactPrice(value: number): string {
  if (!Number.isFinite(value)) return "—";
  return Intl.NumberFormat("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 4 }).format(value);
}

/** Inline trend glyph beside the hero number — not a chart, just direction at a glance. */
function MiniSparkline({ prices, direction }: { prices: number[]; direction: "up" | "down" | "flat" }) {
  if (prices.length < 2) return null;
  const min = Math.min(...prices);
  const max = Math.max(...prices);
  const range = max - min || 1;
  const width = 72;
  const height = 28;
  const stepX = width / (prices.length - 1);
  const points = prices
    .map((price, index) => `${(index * stepX).toFixed(1)},${(height - ((price - min) / range) * height).toFixed(1)}`)
    .join(" ");
  const stroke = direction === "up" ? "#059669" : direction === "down" ? "#dc2626" : "#94a3b8";

  return (
    <svg width={width} height={height} viewBox={`0 0 ${width} ${height}`} className="shrink-0" aria-hidden="true">
      <polyline points={points} fill="none" stroke={stroke} strokeWidth={2} strokeLinecap="round" strokeLinejoin="round" />
    </svg>
  );
}

function segmentClass(active: boolean) {
  return cn(
    "shrink-0 rounded-md px-3 py-1.5 font-medium text-sm transition-colors",
    active
      ? "bg-background text-foreground shadow-sm"
      : "text-muted-foreground hover:text-foreground",
  );
}

type Tone = "positive" | "negative" | "neutral";

function toneText(tone: Tone | undefined) {
  if (tone === "positive") return "text-emerald-600 dark:text-emerald-400";
  if (tone === "negative") return "text-red-600 dark:text-red-400";
  return "text-foreground";
}

function StatTile({
  label,
  value,
  tone,
  icon: Icon,
  onClick,
}: {
  label: string;
  value: string;
  tone?: Tone;
  icon: React.ComponentType<{ className?: string }>;
  /** Opens the underlying positions behind this tile. */
  onClick: () => void;
}) {
  return (
    <button
      type="button"
      onClick={onClick}
      className="group flex min-w-0 flex-col gap-2 rounded-xl border border-border/60 bg-background/70 p-4 text-left shadow-xs backdrop-blur-sm transition-all hover:-translate-y-px hover:border-emerald-500/40 hover:shadow-md focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
    >
      <span className="flex items-center justify-between gap-2">
        <span className="font-medium text-[11px] text-muted-foreground uppercase tracking-wider">{label}</span>
        <span
          className={cn(
            "flex size-7 shrink-0 items-center justify-center rounded-lg",
            tone === "positive" && "bg-emerald-500/10 text-emerald-600 dark:text-emerald-400",
            tone === "negative" && "bg-red-500/10 text-red-600 dark:text-red-400",
            (!tone || tone === "neutral") && "bg-muted text-muted-foreground group-hover:text-foreground",
          )}
        >
          <Icon className="size-3.5" />
        </span>
      </span>
      <span className={cn("break-words font-semibold text-base tracking-tight tabular-nums sm:text-lg", toneText(tone))}>
        {value || "—"}
      </span>
    </button>
  );
}

/** Secondary figures not already in the hero (prices, income, local-currency values). */
function DetailsGrid({ tab, onOpenPositions }: { tab: AssetAnalyticsAssetTab; onOpenPositions: () => void }) {
  const items = groupMetrics(tab.metrics).flatMap((section) => section.items);
  if (items.length === 0) return null;

  return (
    <div className="grid grid-cols-[repeat(auto-fit,minmax(9rem,1fr))] gap-3">
      {items.map((item) => (
        <button
          key={item.label}
          type="button"
          onClick={onOpenPositions}
          className="flex min-w-0 flex-col gap-1 rounded-xl border border-border/60 bg-muted/20 px-4 py-3 text-left transition-colors hover:bg-muted/50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
        >
          <span className="text-muted-foreground text-xs">{item.label}</span>
          <span className="truncate font-semibold text-sm tabular-nums">{item.value || "—"}</span>
        </button>
      ))}
    </div>
  );
}

/**
 * Accumulators / decumulators on the underlying (expired included, tagged) with their Live-only
 * totals. Contracts are not an asset class of the ticker, so this decides whether the Accu / Decu
 * tab shows and feeds its hero figures.
 */
function useAccumulatorContracts(tickerId: string, banks: string[]) {
  const banksKey = banks.join("|");
  const [state, setState] = React.useState<{
    key: string;
    rows: AssetAnalyticsPositionRow[];
    totals: AssetAnalyticsPositionsTotals | null;
  }>({ key: "", rows: [], totals: null });
  const key = `${tickerId}|${banksKey}`;

  React.useEffect(() => {
    if (!tickerId) return;
    const controller = new AbortController();
    fetchAssetAnalyticsPositionsClient(tickerId, ACCUMULATORS_TAB_SLUG, banks, controller.signal, true)
      .then((data) => setState({ key, rows: data.accumulators, totals: data.accumulatorTotals }))
      .catch(() => {
        if (!controller.signal.aborted) setState({ key, rows: [], totals: null });
      });
    return () => controller.abort();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [key]);

  // A stale ticker's contracts never leak into the next one while it loads.
  return state.key === key ? state : { key, rows: [], totals: null };
}

function TickerSection({
  group,
  banks,
}: {
  group: ReturnType<typeof filterTickerGroups>[0];
  banks: string[];
}) {
  const [positionsTarget, setPositionsTarget] = React.useState<AssetAnalyticsPositionsTarget | null>(null);
  // Expired structures / accumulators: off by default; one switch for the panel and the sheet.
  const [includeExpired, setIncludeExpired] = React.useState(false);
  const targetFor = React.useCallback(
    (tab: AssetAnalyticsAssetTab): AssetAnalyticsPositionsTarget => ({
      tickerId: group.tickerId,
      // Matches the backend's lowercased asset-class title (mTitle) — titleCase() reversed.
      assetClassSlug: tab.assetClass.toLowerCase(),
      tickerDisplay: tab.tickerDisplay,
      productName: tab.productName,
      assetClass: tab.assetClass,
    }),
    [group.tickerId],
  );

  const accumulators = useAccumulatorContracts(group.tickerId, banks);
  const accuOpen = accumulators.rows.filter((row) => row.status !== "Expired");
  const hasAccumulators = accumulators.rows.length > 0;

  const sortedTabs = sortAssetTabs(group.tabs);
  const graphTab = sortedTabs.find((t) => t.hasChart);
  const firstTabId = graphTab ? "graph" : sortedTabs[0]?.assetClass.toLowerCase().replace(/\s+/g, "-") ?? "";
  const [activeTab, setActiveTab] = React.useState(firstTabId);
  const [syncedTabKey, setSyncedTabKey] = React.useState(`${group.tickerId}|${firstTabId}`);
  const tabKey = `${group.tickerId}|${firstTabId}`;
  if (tabKey !== syncedTabKey) {
    setSyncedTabKey(tabKey);
    setActiveTab(firstTabId);
  }

  const accuActive = activeTab === ACCUMULATORS_TAB_SLUG && hasAccumulators;
  const displayTab =
    activeTab === "graph" || accuActive
      ? null
      : sortedTabs.find((t) => t.assetClass.toLowerCase().replace(/\s+/g, "-") === activeTab) ?? sortedTabs[0] ?? null;

  // Hero KPIs follow the selected asset-class tab; on Graph they follow the primary tab.
  const focusTab = displayTab ?? sortedTabs[0] ?? null;
  const gainTone = focusTab ? parseGainTone(focusTab.metrics.gainLossPercent) : "neutral";

  const periodDelta = React.useMemo(() => {
    const history = group.priceHistory;
    if (history.length < 2) return null;
    const first = history[0];
    const last = history[history.length - 1];
    if (!first.price) return null;
    const changePercent = ((last.price - first.price) / first.price) * 100;
    const direction = changePercent > 0 ? "up" : changePercent < 0 ? "down" : "flat";
    const sign = changePercent > 0 ? "+" : "";
    const from = new Date(`${first.date}T00:00:00`);
    const to = new Date(`${last.date}T00:00:00`);
    const range =
      Number.isNaN(from.getTime()) || Number.isNaN(to.getTime())
        ? `${first.date} – ${last.date}`
        : from.getFullYear() === to.getFullYear()
          ? `${format(from, "MMM d")} – ${format(to, "MMM d, yyyy")}`
          : `${format(from, "MMM d, yyyy")} – ${format(to, "MMM d, yyyy")}`;
    const pct = `${sign}${changePercent.toFixed(2)}%`;
    return {
      direction,
      label: `Close ${pct}`,
      range,
      title: `Underlying close ${pct} from ${range}. This is the price chart, not the holding's gain or loss.`,
    } as const;
  }, [group.priceHistory]);

  const tickerLabel = focusTab?.tickerDisplay ?? group.name.split(" ")[0] ?? "";
  // Avatar from the instrument name ("APPLE INC" → "AP"), not the ISIN's country prefix.
  const mark = (group.name.replace(/[^A-Za-z0-9]/g, "").slice(0, 2) || tickerLabel.slice(0, 2)).toUpperCase();
  const accuTarget: AssetAnalyticsPositionsTarget = {
    tickerId: group.tickerId,
    assetClassSlug: ACCUMULATORS_TAB_SLUG,
    tickerDisplay: tickerLabel,
    productName: focusTab?.productName ?? group.name,
    assetClass: ACCUMULATORS_TAB_LABEL,
  };
  const openPositions = accuActive
    ? () => setPositionsTarget(accuTarget)
    : focusTab
      ? () => setPositionsTarget(targetFor(focusTab))
      : () => {};
  const GainIcon = gainTone === "negative" ? ArrowDownRight : ArrowUpRight;
  const accuMtm = accumulators.totals?.marketValueUsd ?? 0;
  const accuTone: Tone = accuMtm > 0 ? "positive" : accuMtm < 0 ? "negative" : "neutral";
  const accuKinds = accuOpen.reduce(
    (counts, row) => {
      if (row.kind === "decumulator") counts.decu += 1;
      else counts.accu += 1;
      return counts;
    },
    { accu: 0, decu: 0 },
  );

  return (
    <div className="space-y-4">
      <Card className="overflow-hidden rounded-2xl border-border/60 shadow-sm">
        <CardContent className="flex flex-col gap-0 p-0">
          <div className="relative flex flex-col gap-6 p-5 sm:p-7">
            <div className="pointer-events-none absolute inset-0 bg-gradient-to-br from-emerald-500/[0.08] via-transparent to-sky-500/[0.05] dark:from-emerald-400/[0.10] dark:to-sky-400/[0.06]" />
            <div className="relative flex flex-col gap-6 lg:flex-row lg:items-end lg:justify-between">
              <div className="flex min-w-0 items-center gap-4">
                <div className="flex size-14 shrink-0 items-center justify-center rounded-2xl bg-gradient-to-br from-emerald-500 to-teal-600 font-semibold text-lg text-white tracking-tight shadow-lg shadow-emerald-600/20">
                  {mark}
                </div>
                <div className="min-w-0 space-y-2">
                  <h2 className="truncate font-semibold text-2xl tracking-tight">{group.name}</h2>
                  <div className="flex flex-wrap items-center gap-1.5">
                    <span className="rounded-md border border-border/60 bg-background/70 px-1.5 py-0.5 font-mono text-[11px] text-muted-foreground">
                      {tickerLabel}
                    </span>
                    {focusTab ? (
                      <Badge variant="secondary" className="rounded-md font-normal text-xs">
                        {accuActive ? accuTarget.assetClass : focusTab.assetClass}
                      </Badge>
                    ) : null}
                    {accuActive ? (
                      <button
                        type="button"
                        onClick={openPositions}
                        className="inline-flex items-center gap-0.5 rounded-md bg-emerald-600/10 px-2 py-0.5 font-medium text-emerald-700 text-xs transition-colors hover:bg-emerald-600/20 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring dark:text-emerald-400"
                      >
                        {accuOpen.length} contract{accuOpen.length === 1 ? "" : "s"}
                        <ChevronRight className="size-3" />
                      </button>
                    ) : focusTab && focusTab.lotCount > 0 ? (
                      <button
                        type="button"
                        onClick={openPositions}
                        className="inline-flex items-center gap-0.5 rounded-md bg-emerald-600/10 px-2 py-0.5 font-medium text-emerald-700 text-xs transition-colors hover:bg-emerald-600/20 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring dark:text-emerald-400"
                      >
                        {focusTab.lotCount} position{focusTab.lotCount === 1 ? "" : "s"}
                        <ChevronRight className="size-3" />
                      </button>
                    ) : null}
                  </div>
                </div>
              </div>
              {accuActive ? (
                <div className="relative shrink-0 lg:text-right">
                  <p className="font-medium text-[11px] text-muted-foreground uppercase tracking-wider">Mark-to-market · {group.reportingCurrency}</p>
                  <button
                    type="button"
                    onClick={openPositions}
                    className={cn(
                      "mt-1.5 rounded font-semibold text-3xl leading-none tracking-tight tabular-nums transition-opacity hover:opacity-80 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring sm:text-4xl",
                      toneText(accuTone),
                    )}
                  >
                    {accumulators.totals?.marketValueUsdFmt ?? "—"}
                  </button>
                  <div className="mt-2.5 flex flex-wrap items-center gap-1.5 lg:justify-end">
                    <span className="inline-flex items-center rounded-full border border-border/60 bg-background/70 px-2.5 py-1 font-medium text-muted-foreground text-xs">
                      Live contracts only
                    </span>
                  </div>
                </div>
              ) : focusTab ? (
                <div className="relative shrink-0 lg:text-right">
                  <p className="font-medium text-[11px] text-muted-foreground uppercase tracking-wider">Market value · {group.reportingCurrency}</p>
                  <div className="mt-1.5 flex items-center gap-3 lg:justify-end">
                    {focusTab.hasChart && periodDelta ? (
                      <MiniSparkline prices={group.priceHistory.map((p) => p.price)} direction={periodDelta.direction} />
                    ) : null}
                    <button
                      type="button"
                      onClick={openPositions}
                      className="rounded font-semibold text-3xl leading-none tracking-tight tabular-nums transition-colors hover:text-emerald-600 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring sm:text-4xl dark:hover:text-emerald-400"
                    >
                      {focusTab.metrics.totalMarketValueUsd}
                    </button>
                  </div>
                  <div className="mt-2.5 flex flex-wrap items-center gap-1.5 lg:justify-end">
                    <span
                      title="Unrealized return on cost: Unrealized P&L ÷ Purchase value"
                      className={cn(
                        "inline-flex items-center gap-1 rounded-full px-2.5 py-1 font-semibold text-xs tabular-nums",
                        gainTone === "positive" && "bg-emerald-500/10 text-emerald-700 dark:text-emerald-400",
                        gainTone === "negative" && "bg-red-500/10 text-red-700 dark:text-red-400",
                        gainTone === "neutral" && "bg-muted text-muted-foreground",
                      )}
                    >
                      {gainTone !== "neutral" ? <GainIcon className="size-3" /> : null}
                      {focusTab.metrics.gainLossPercent}
                      <span className="font-normal opacity-80">return</span>
                    </span>
                    {focusTab.hasChart && periodDelta ? (
                      <span
                        title={periodDelta.title}
                        className={cn(
                          "inline-flex max-w-full flex-wrap items-center gap-x-1.5 gap-y-0.5 rounded-full border border-border/60 bg-background/70 px-2.5 py-1 font-medium text-xs backdrop-blur-md",
                          periodDelta.direction === "up" && "text-emerald-700 dark:text-emerald-400",
                          periodDelta.direction === "down" && "text-red-700 dark:text-red-400",
                          periodDelta.direction === "flat" && "text-muted-foreground",
                        )}
                      >
                        <span>{periodDelta.label}</span>
                        <span className="font-normal text-muted-foreground">{periodDelta.range}</span>
                      </span>
                    ) : null}
                  </div>
                </div>
              ) : null}
            </div>

            {accuActive ? (
              <div className="relative grid grid-cols-2 gap-3 lg:grid-cols-4">
                <StatTile
                  label="Mark-to-market"
                  value={accumulators.totals?.marketValueUsdFmt ?? "—"}
                  tone={accuTone}
                  icon={accuTone === "negative" ? TrendingDown : TrendingUp}
                  onClick={openPositions}
                />
                <StatTile
                  label="Live contracts"
                  value={String(accumulators.rows.filter((row) => row.status === "Live").length)}
                  icon={Repeat2}
                  onClick={openPositions}
                />
                <StatTile
                  label="Quantity (live)"
                  value={formatQuantity(accumulators.totals?.quantity ?? 0)}
                  icon={Hash}
                  onClick={openPositions}
                />
                <StatTile
                  label="Accu · Decu"
                  value={`${accuKinds.accu} · ${accuKinds.decu}`}
                  icon={Layers3}
                  onClick={openPositions}
                />
              </div>
            ) : focusTab ? (
              <div className="relative grid grid-cols-2 gap-3 lg:grid-cols-4">
                <StatTile
                  label="Purchase value"
                  value={focusTab.metrics.totalPurchaseValueUsd}
                  icon={Wallet}
                  onClick={openPositions}
                />
                <StatTile
                  label="Unrealized P&L"
                  value={focusTab.metrics.unrealizedGainLoss}
                  tone={parseGainTone(focusTab.metrics.unrealizedGainLoss)}
                  icon={parseGainTone(focusTab.metrics.unrealizedGainLoss) === "negative" ? TrendingDown : TrendingUp}
                  onClick={openPositions}
                />
                <StatTile
                  label="XIRR"
                  value={focusTab.metrics.xirr}
                  tone={parseGainTone(focusTab.metrics.xirr)}
                  icon={Percent}
                  onClick={openPositions}
                />
                <StatTile label="Quantity" value={focusTab.metrics.totalQuantity} icon={Hash} onClick={openPositions} />
              </div>
            ) : null}
          </div>

          <div className="flex items-center border-border/60 border-t bg-muted/20 px-5 py-3 sm:px-7">
            <div
              className="inline-flex max-w-full gap-0.5 overflow-x-auto rounded-lg bg-muted/70 p-1 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden"
              role="tablist"
            >
              {graphTab ? (
                <button
                  type="button"
                  role="tab"
                  aria-selected={activeTab === "graph"}
                  onClick={() => setActiveTab("graph")}
                  className={segmentClass(activeTab === "graph")}
                >
                  <span className="inline-flex items-center gap-1.5">
                    <LineChart className="size-3.5" />
                    Graph
                  </span>
                </button>
              ) : null}
              {sortedTabs.map((tab) => {
                const id = tab.assetClass.toLowerCase().replace(/\s+/g, "-");
                return (
                  <button
                    key={id}
                    type="button"
                    role="tab"
                    aria-selected={activeTab === id}
                    onClick={() => setActiveTab(id)}
                    className={segmentClass(activeTab === id)}
                  >
                    <span className="inline-flex items-center gap-1.5">
                      {tab.assetClass}
                      <span
                        className={cn(
                          "rounded-full px-1.5 text-[10px] tabular-nums",
                          activeTab === id ? "bg-emerald-600/10 text-emerald-700 dark:text-emerald-400" : "bg-foreground/[0.06]",
                        )}
                      >
                        {tab.lotCount}
                      </span>
                    </span>
                  </button>
                );
              })}
              {hasAccumulators ? (
                <button
                  type="button"
                  role="tab"
                  aria-selected={accuActive}
                  onClick={() => setActiveTab(ACCUMULATORS_TAB_SLUG)}
                  className={segmentClass(accuActive)}
                >
                  <span className="inline-flex items-center gap-1.5">
                    <Repeat2 className="size-3.5" />
                    {ACCUMULATORS_TAB_LABEL}
                    <span
                      className={cn(
                        "rounded-full px-1.5 text-[10px] tabular-nums",
                        accuActive ? "bg-emerald-600/10 text-emerald-700 dark:text-emerald-400" : "bg-foreground/[0.06]",
                      )}
                    >
                      {accuOpen.length}
                    </span>
                  </span>
                </button>
              ) : null}
            </div>
          </div>

          <div className="px-5 py-5 sm:px-7 sm:py-6">
            {activeTab === "graph" && graphTab ? (
              <div className="space-y-3">
                <p className="text-muted-foreground text-xs">
                  <span className="font-mono text-foreground">{graphTab.chartSymbol ?? graphTab.tickerCode}</span>
                  {group.priceHistory.length > 1
                    ? ` · Daily close · ${group.priceHistory.length} points`
                    : group.priceHistory.length === 1
                      ? " · Single price point on record"
                      : " · No price history yet"}
                </p>
                {group.priceHistory.length > 1 ? (
                  <ChartContainer config={chartConfig} className="aspect-[2.4/1] min-h-[260px] w-full">
                    <AreaChart
                      data={group.priceHistory.map((point) => ({ date: formatChartDate(point.date), price: point.price }))}
                      margin={{ left: 8, right: 8, top: 8, bottom: 0 }}
                    >
                      <defs>
                        <linearGradient id="priceFill" x1="0" y1="0" x2="0" y2="1">
                          <stop offset="0%" stopColor="hsl(142 71% 45%)" stopOpacity={0.35} />
                          <stop offset="100%" stopColor="hsl(142 71% 45%)" stopOpacity={0} />
                        </linearGradient>
                      </defs>
                      <CartesianGrid vertical={false} strokeDasharray="3 3" className="stroke-border/60" />
                      <XAxis
                        dataKey="date"
                        tickLine={false}
                        axisLine={false}
                        tick={{ fontSize: 11 }}
                        interval="preserveStartEnd"
                        minTickGap={32}
                      />
                      <YAxis
                        tickLine={false}
                        axisLine={false}
                        tick={{ fontSize: 11 }}
                        width={48}
                        domain={["auto", "auto"]}
                        tickFormatter={formatChartPrice}
                      />
                      <ChartTooltip content={<ChartTooltipContent />} />
                      <Area type="monotone" dataKey="price" stroke="hsl(142 71% 45%)" fill="url(#priceFill)" strokeWidth={2} />
                    </AreaChart>
                  </ChartContainer>
                ) : group.priceHistory.length === 1 ? (
                  // A line has nothing to connect with a single point, so draw it as a
                  // single-column bar chart instead of an (empty-looking) line/area chart —
                  // still a real graph, just the right mark for one value.
                  <ChartContainer config={chartConfig} className="aspect-[2.4/1] min-h-[260px] w-full">
                    <BarChart
                      data={group.priceHistory.map((point) => ({ date: formatChartDate(point.date), price: point.price }))}
                      margin={{ left: 8, right: 8, top: 24, bottom: 0 }}
                    >
                      <CartesianGrid vertical={false} strokeDasharray="3 3" className="stroke-border/60" />
                      <XAxis dataKey="date" tickLine={false} axisLine={false} tick={{ fontSize: 11 }} />
                      <YAxis
                        tickLine={false}
                        axisLine={false}
                        tick={{ fontSize: 11 }}
                        width={48}
                        domain={[0, (max: number) => max * 1.2]}
                        tickFormatter={formatChartPrice}
                      />
                      <ChartTooltip content={<ChartTooltipContent />} />
                      <Bar dataKey="price" fill="hsl(142 71% 45%)" radius={[4, 4, 0, 0]} maxBarSize={64}>
                        <LabelList
                          dataKey="price"
                          position="top"
                          formatter={(value: unknown) => formatExactPrice(Number(value))}
                          className="fill-foreground font-medium text-xs"
                        />
                      </Bar>
                    </BarChart>
                  </ChartContainer>
                ) : (
                  <div className="flex min-h-[180px] items-center justify-center rounded-lg border border-dashed">
                    <p className="text-muted-foreground text-sm">No price history available for this ticker yet.</p>
                  </div>
                )}
              </div>
            ) : accuActive ? (
              <DerivativeLedgerBrief
                tickerId={group.tickerId}
                scorecard="accu"
                includeExpired={includeExpired}
                onIncludeExpiredChange={setIncludeExpired}
              />
            ) : displayTab ? (
              <div className="space-y-4">
                <DetailsGrid tab={displayTab} onOpenPositions={() => setPositionsTarget(targetFor(displayTab))} />
                {displayTab.assetClass.toLowerCase() === STRUCTURE_TAB_SLUG ? (
                  <DerivativeLedgerBrief
                    tickerId={group.tickerId}
                    scorecard="structure"
                    includeExpired={includeExpired}
                    onIncludeExpiredChange={setIncludeExpired}
                  />
                ) : displayTab.assetClass.toLowerCase() === DERIVATIVES_TAB_SLUG ? (
                  <DerivativeLedgerBrief tickerId={group.tickerId} />
                ) : null}
              </div>
            ) : null}
          </div>
        </CardContent>
      </Card>

      <AssetAnalyticsPositionsSheet
        open={positionsTarget !== null}
        onOpenChange={(nextOpen) => {
          if (!nextOpen) setPositionsTarget(null);
        }}
        target={positionsTarget}
        banks={banks}
        includeExpired={includeExpired}
        onIncludeExpiredChange={setIncludeExpired}
      />
    </div>
  );
}

/**
 * Filters the report starts from (and falls back to on scope / bank-mode resets): the default
 * ticker saved in Settings, overridden by `?ticker=` (Settings' "Open Asset Analytics" preview).
 */
function startingFilters(): AssetAnalyticsFilters {
  const initial = getInitialAssetAnalyticsFilters();
  const fromUrl = tickerFromSearch(window.location.search);
  return fromUrl ? { ...initial, ticker: fromUrl } : initial;
}

export function AssetAnalyticsView() {
  const params = useParams<{ tenant?: string }>();
  const tenant = typeof params.tenant === "string" && params.tenant.trim() ? params.tenant.trim() : resolveCustomerTenant();
  const { layout, loading: layoutLoading } = useDefaultLayout(tenant);
  // Defaults only — localStorage is applied in useEffect to keep SSR and hydration in sync.
  const [filters, setFilters] = React.useState<AssetAnalyticsFilters>(defaultAssetAnalyticsFilters);
  const [appliedFilters, setAppliedFilters] = React.useState<AssetAnalyticsFilters>(defaultAssetAnalyticsFilters);
  const [settingsReady, setSettingsReady] = React.useState(false);
  const [refreshKey, setRefreshKey] = React.useState(0);

  // Resets keep the default ticker — only bank / asset-class selections are scope-bound.
  const refreshForPortfolioScope = React.useCallback(() => {
    const initial = startingFilters();
    setFilters(initial);
    setAppliedFilters(initial);
    setRefreshKey((value) => value + 1);
  }, []);
  useImpersonationScopeRefresh(refreshForPortfolioScope);

  // Parent/Child bank ids are not interchangeable — a selection made under one
  // mode is meaningless (and can silently under/over-scope) under the other.
  useReportBankModeInvalidation(() => {
    const initial = startingFilters();
    setFilters(initial);
    setAppliedFilters(initial);
    setRefreshKey((value) => value + 1);
  });

  React.useEffect(() => {
    let cancelled = false;
    const syncFromSettings = () => {
      const initial = startingFilters();
      setFilters(initial);
      setAppliedFilters(initial);
      setSettingsReady(true);
    };

    // The account-saved default (survives incognito / cleared data) wins over this browser's
    // cached copy; an explicit ?ticker= link needs no lookup.
    if (tickerFromSearch(window.location.search)) {
      syncFromSettings();
    } else {
      void fetchReportPrefsClient(tenant).then((serverPrefs) => {
        if (cancelled) return;
        const serverTicker = serverPrefs?.default_analytics_ticker ?? "";
        if (serverTicker && serverTicker !== loadUserReportSettings().defaultAnalyticsTicker) {
          saveUserReportSettings({ ...loadUserReportSettings(), defaultAnalyticsTicker: serverTicker });
        }
        syncFromSettings();
      });
    }

    window.addEventListener("report-settings-updated", syncFromSettings);
    return () => {
      cancelled = true;
      window.removeEventListener("report-settings-updated", syncFromSettings);
    };
  }, [tenant]);

  const { groups, tickerOptions, bankOptions, assetClassOptions, isLoading, errorMessage } = useAssetAnalyticsList(
    appliedFilters.ticker,
    appliedFilters.banks,
    refreshKey,
    settingsReady,
  );

  const { catalog: fetchedAssetClassCatalog } = useAssetClassFilterCatalog(layout, assetClassOptions, {
    enabled: !layoutLoading,
  });
  const assetClassCatalog = React.useMemo(
    () =>
      catalogForDisplay(fetchedAssetClassCatalog, assetClassOptions, {
        preferExistingOnDefault: !layoutLoading && layout === "default",
        ready: !layoutLoading,
      }),
    [fetchedAssetClassCatalog, assetClassOptions, layout, layoutLoading],
  );

  // Re-sanitize the asset-class selection whenever the catalog changes (adjusted during render, not in an effect).
  if (useChanged(assetClassCatalog)) {
    setAppliedFilters((prev) => {
      const next = sanitizeAssetClassSelection(prev.assetClasses, assetClassCatalog, { defaultToAll: false });
      if (sameAssetClassSelection(next, prev.assetClasses)) return prev;
      return { ...prev, assetClasses: next };
    });
    setFilters((prev) => {
      const next = sanitizeAssetClassSelection(prev.assetClasses, assetClassCatalog, { defaultToAll: false });
      if (sameAssetClassSelection(next, prev.assetClasses)) return prev;
      return { ...prev, assetClasses: next };
    });
  }

  const filteredGroups = React.useMemo(
    () => filterTickerGroups(groups, appliedFilters, assetClassCatalog),
    [groups, appliedFilters, assetClassCatalog],
  );

  const selectedTickerLabel = React.useMemo(() => {
    if (!appliedFilters.ticker) return null;
    const match = tickerOptions.find((option) => option.value === appliedFilters.ticker);
    if (!match || match.name === match.value) return appliedFilters.ticker;
    return `${match.name} · ${match.value}`;
  }, [appliedFilters.ticker, tickerOptions]);

  const hasActiveFilters = hasActiveAssetAnalyticsFilters(appliedFilters);
  const showResults = Boolean(appliedFilters.ticker) && filteredGroups.length > 0;

  const applyFilters = (next: AssetAnalyticsFilters) => {
    setFilters(next);
    setAppliedFilters(next);
    if (next.ticker) toast.success("Asset analytics loaded");
  };

  return (
    <div className="flex min-w-0 flex-col gap-5" key={refreshKey}>
      <div className="flex flex-col gap-3 border-b pb-4 lg:flex-row lg:items-center lg:justify-between">
        <div className="min-w-0 space-y-1">
          <div className="flex flex-wrap items-center gap-2">
            <h1 className="font-semibold text-2xl tracking-tight">Asset Analytics</h1>
            <Badge variant="secondary" className="gap-1 font-normal text-muted-foreground">
              <Sparkles className="size-3" />
              Beta
            </Badge>
          </div>
          <p className="text-muted-foreground text-sm">
            {selectedTickerLabel ?? "Per-ticker metrics by asset class and custodian bank"}
            {hasActiveFilters ? " · filtered" : ""}
            {isLoading ? " · Loading…" : ""}
          </p>
          {errorMessage ? <p className="text-destructive text-sm">{errorMessage}</p> : null}
        </div>

        <div className="flex shrink-0 items-center gap-2">
          <AssetAnalyticsFiltersDialog
            filters={filters}
            onApply={applyFilters}
            tickerOptions={tickerOptions}
            bankOptions={bankOptions}
            assetClassOptions={assetClassOptions}
            isLoadingOptions={isLoading}
          />
          <div className="inline-flex items-center overflow-hidden rounded-lg border border-border/70 bg-background">
            <Button
              variant="ghost"
              size="icon"
              className="size-9 rounded-none"
              disabled={isLoading}
              onClick={() => {
                setRefreshKey((k) => k + 1);
                toast.success("Report refreshed");
              }}
            >
              <RefreshCw className={cn("size-4", isLoading && "animate-spin")} />
              <span className="sr-only">Refresh</span>
            </Button>
            <Button variant="ghost" size="icon" className="size-9 rounded-none border-border/70 border-l" asChild>
              <Link href="/customer/reports">
                <X className="size-4" />
                <span className="sr-only">Close</span>
              </Link>
            </Button>
          </div>
        </div>
      </div>

      {!appliedFilters.ticker ? (
        <Card className="border-dashed">
          <CardHeader className="text-center">
            <CardTitle className="text-base">Pick a ticker to get started</CardTitle>
            <CardDescription>
              Use the filters button to search for an instrument, or set a default ticker in{" "}
              <Link
                href={`/customer/${tenant}/settings?section=asset-analytics`}
                className="text-primary underline-offset-4 hover:underline"
              >
                Settings
              </Link>
              .
            </CardDescription>
          </CardHeader>
        </Card>
      ) : !showResults && !isLoading ? (
        <Card className="border-dashed">
          <CardHeader className="text-center">
            <CardTitle className="text-base">No results</CardTitle>
            <CardDescription>
              No holdings for this ticker with the current asset class or bank filters. Broaden your selection and
              try again.
            </CardDescription>
          </CardHeader>
        </Card>
      ) : (
        filteredGroups.map((group) => (
          <TickerSection key={group.tickerId} group={group} banks={appliedFilters.banks} />
        ))
      )}
    </div>
  );
}
