"use client";

import * as React from "react";

import { useRenderEffect } from "@/hooks/use-changed";
import Link from "next/link";
import { format, parseISO } from "date-fns";
import {
  ArrowDownUp,
  Building2,
  ChevronDown,
  ChevronsDownUp,
  Download,
  Globe,
  Layers,
  LayoutGrid,
  MapPin,
  Printer,
  RefreshCw,
  Tag,
  X,
} from "lucide-react";
import { toast } from "sonner";

import { toastApiError } from "@/lib/toast-api-error";

import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Card, CardContent } from "@/components/ui/card";
import {
  Collapsible,
  CollapsibleContent,
  CollapsibleTrigger,
} from "@/components/ui/collapsible";
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { cn } from "@/lib/utils";

import { AsOfSnapshotBanner } from "@/app/customer/[tenant]/reports/_shared/components/as-of-snapshot-banner";
import { ReportLoadingPanel } from "@/app/customer/[tenant]/reports/_shared/components/report-loading-panel";
import { ReportSortableHeaderButton } from "@/app/customer/[tenant]/reports/_shared/components/report-table-chrome";
import { nextReportSort } from "@/app/customer/[tenant]/reports/_shared/lib/report-sort";
import { deriveAsOfViewState, formatYmd } from "@/app/customer/[tenant]/reports/_shared/lib/as-of-snapshot-view";
import { useAssetClassFilterCatalog } from "@/app/customer/[tenant]/reports/_shared/lib/use-asset-class-filter-catalog";
import { expandToUnderlierLabels } from "@/app/customer/[tenant]/reports/_shared/lib/asset-class-filter-catalog";
import { presetForDefaultLayout } from "@/app/customer/_lib/default-layout";
import { useDefaultLayout } from "@/app/customer/_lib/use-default-layout";
import { resolveCustomerTenant } from "@/app/customer/_lib/resolve-customer-tenant";
import { useOverallSummaryAsOf, type AsOfSnapshotClient } from "../../../overall-summary/_lib/use-overall-summary-asof";
import {
  fetchBankWiseAumAsOfStatusClient,
  kickoffBankWiseAumAsOfClient,
} from "../../_lib/bank-wise-aum-asof-api";
import { useBankWiseAumList } from "../../_lib/use-bank-wise-aum-list";
import { BankWiseAumFiltersDialog } from "./bank-wise-aum-filters-dialog";
import {
  type BankAumBreakdownTab,
  type BankAumMetricRow,
  type BankAumSummary,
  type BankWiseAumAssetClassMode,
  type BankWiseAumFilters,
  bankWiseAumMeta,
  defaultBankWiseAumFilters,
  hasActiveBankWiseAumFilters,
  isBankWiseAumAssetClassMode,
  isCompareMode,
} from "./schema";
import { useImpersonationScopeRefresh } from "@/app/customer/_lib/admin/use-impersonation-scope-refresh";
import { useReportBankModeInvalidation } from "@/app/customer/_lib/use-report-bank-mode-invalidation";
import { useAumCalcMode } from "@/app/customer/_lib/use-aum-calc-mode";
import {
  type BankAumSortKey,
  type BankAumSortState,
  computeDiff,
  computeGrandTotals,
  filterBanks,
  formatDiff,
  formatDiffPct,
  formatPeriodLabel,
  sortBankAumRows,
  sortBanksByAum,
  sumMetrics,
} from "./utils";
import { formatMoneyOrDash } from "@/lib/format/numbers";
import { useParams } from "next/navigation";
import { useLatestRef } from "@/hooks/use-latest-ref";

const innerTabs: {
  id: BankAumBreakdownTab;
  label: string;
  nameHeader: string;
  icon: React.ReactNode;
  rows: (bank: BankAumSummary) => BankAumMetricRow[];
}[] = [
  {
    id: "assetClass",
    label: "Asset class",
    nameHeader: "Asset class",
    icon: <LayoutGrid className="size-3.5" />,
    rows: (bank) => bank.assetClassRows,
  },
  {
    id: "assetType",
    label: "Asset type",
    nameHeader: "Asset type",
    icon: <Tag className="size-3.5" />,
    rows: (bank) => bank.assetTypeRows,
  },
  {
    id: "sector",
    label: "Sector",
    nameHeader: "Sector",
    icon: <Layers className="size-3.5" />,
    rows: (bank) => bank.sectorRows,
  },
  {
    id: "geography",
    label: "Geography",
    nameHeader: "Country",
    icon: <Globe className="size-3.5" />,
    rows: (bank) => bank.geographyRows,
  },
];

function pnlClass(value: number) {
  if (value > 0) return "text-emerald-600 dark:text-emerald-400 font-semibold";
  if (value < 0) return "text-red-600 dark:text-red-400 font-semibold";
  return "text-muted-foreground";
}

function BreakdownTable({
  rows,
  compare,
  labelFrom,
  labelTo,
  nameHeader,
  sort,
  onSort,
}: {
  rows: BankAumMetricRow[];
  compare: boolean;
  labelFrom: string;
  labelTo: string;
  nameHeader: string;
  sort: BankAumSortState;
  onSort: (key: BankAumSortKey) => void;
}) {
  const totals = sumMetrics(rows);
  const sortedRows = React.useMemo(() => sortBankAumRows(rows, sort), [rows, sort]);
  const orderFor = (key: BankAumSortKey) => (sort.key === key ? sort.order : null);
  const ariaSortFor = (key: BankAumSortKey) =>
    orderFor(key) === "asc" ? "ascending" : orderFor(key) === "desc" ? "descending" : "none";

  return (
    <div className="overflow-x-auto rounded-lg border border-border/80">
      <Table>
        <TableHeader>
          <TableRow className="bg-slate-800 hover:bg-slate-800">
            <TableHead className="min-w-[200px] text-white">{nameHeader}</TableHead>
            {compare ? (
              <>
                <TableHead className="bg-violet-900/80 text-right text-violet-200 text-xs">{labelFrom}</TableHead>
                <TableHead className="bg-emerald-900/80 text-right text-emerald-200 text-xs">{labelTo}</TableHead>
                <TableHead className="bg-slate-700 text-right text-slate-200 text-xs">Change</TableHead>
                <TableHead className="bg-slate-700 text-right text-slate-200 text-xs">% Chg</TableHead>
              </>
            ) : (
              <>
                <TableHead className="text-right text-white text-xs" aria-sort={ariaSortFor("purchaseValue")}>
                  <ReportSortableHeaderButton
                    label="Purchase value"
                    onClick={() => onSort("purchaseValue")}
                    sortOrder={orderFor("purchaseValue")}
                    className="ml-auto text-xs text-white hover:bg-white/10 hover:text-white"
                    iconClassName="text-white"
                  />
                </TableHead>
                <TableHead
                  className="bg-emerald-900/50 text-right text-emerald-100 text-xs"
                  aria-sort={ariaSortFor("marketValue")}
                >
                  <ReportSortableHeaderButton
                    label={`Market value (AUM)${labelTo !== "Current" ? ` · ${labelTo}` : ""}`}
                    onClick={() => onSort("marketValue")}
                    sortOrder={orderFor("marketValue")}
                    className="ml-auto text-xs text-emerald-100 hover:bg-white/10 hover:text-emerald-50"
                    iconClassName="text-emerald-100"
                  />
                </TableHead>
              </>
            )}
          </TableRow>
        </TableHeader>
        <TableBody>
          {rows.length === 0 ? (
            <TableRow>
              <TableCell colSpan={compare ? 5 : 3} className="py-10 text-center text-muted-foreground text-sm">
                No data for this breakdown
              </TableCell>
            </TableRow>
          ) : (
            sortedRows.map((row) => {
              const diff = computeDiff(row.d1Aum, row.d2Aum);
              return (
                <TableRow key={row.id} className="even:bg-muted/30">
                  <TableCell className="py-2.5 font-medium text-sm">
                    {row.label}
                    {row.sublabel ? (
                      <Badge variant="outline" className="ml-2 font-normal text-[10px]">
                        {row.sublabel}
                      </Badge>
                    ) : null}
                  </TableCell>
                  {compare ? (
                    <>
                      <TableCell className="py-2.5 text-right text-sm tabular-nums">{formatMoneyOrDash(row.d1Aum)}</TableCell>
                      <TableCell className="py-2.5 text-right text-sm tabular-nums">{formatMoneyOrDash(row.d2Aum)}</TableCell>
                      <TableCell className={cn("bg-muted/40 py-2.5 text-right text-sm tabular-nums", pnlClass(diff))}>
                        {formatDiff(diff)}
                      </TableCell>
                      <TableCell className={cn("bg-muted/40 py-2.5 text-right text-sm tabular-nums", pnlClass(diff))}>
                        {formatDiffPct(row.d1Aum, row.d2Aum)}
                      </TableCell>
                    </>
                  ) : (
                    <>
                      <TableCell className="py-2.5 text-right text-sm tabular-nums">{formatMoneyOrDash(row.d2Pv)}</TableCell>
                      <TableCell className="py-2.5 text-right text-sm tabular-nums">{formatMoneyOrDash(row.d2Aum)}</TableCell>
                    </>
                  )}
                </TableRow>
              );
            })
          )}
          {rows.length > 0 ? (
            <TableRow className="border-t-2 bg-gradient-to-r from-slate-800 to-slate-700 font-semibold hover:from-slate-800 hover:to-slate-700">
              <TableCell className="py-3 text-white">Total AUM</TableCell>
              {compare ? (
                <>
                  <TableCell className="py-3 text-right text-white tabular-nums">{formatMoneyOrDash(totals.d1Aum)}</TableCell>
                  <TableCell className="py-3 text-right text-white tabular-nums">{formatMoneyOrDash(totals.d2Aum)}</TableCell>
                  <TableCell className={cn("py-3 text-right tabular-nums", totals.d2Aum - totals.d1Aum >= 0 ? "text-emerald-300" : "text-red-300")}>
                    {formatDiff(computeDiff(totals.d1Aum, totals.d2Aum))}
                  </TableCell>
                  <TableCell className={cn("py-3 text-right tabular-nums", totals.d2Aum - totals.d1Aum >= 0 ? "text-emerald-300" : "text-red-300")}>
                    {formatDiffPct(totals.d1Aum, totals.d2Aum)}
                  </TableCell>
                </>
              ) : (
                <>
                  <TableCell className="py-3 text-right text-white tabular-nums">{formatMoneyOrDash(totals.d2Pv)}</TableCell>
                  <TableCell className="py-3 text-right text-white tabular-nums">{formatMoneyOrDash(totals.d2Aum)}</TableCell>
                </>
              )}
            </TableRow>
          ) : null}
        </TableBody>
      </Table>
    </div>
  );
}

function BankAccordion({
  bank,
  compare,
  labelFrom,
  labelTo,
  totalAum,
  open,
  onOpenChange,
}: {
  bank: BankAumSummary;
  compare: boolean;
  labelFrom: string;
  labelTo: string;
  totalAum: number;
  open: boolean;
  onOpenChange: (open: boolean) => void;
}) {
  const diff = computeDiff(bank.d1Aum, bank.d2Aum);
  const diffPct = formatDiffPct(bank.d1Aum, bank.d2Aum);
  const pctOfTotal = totalAum > 0 ? (bank.d2Aum / totalAum) * 100 : 0;
  const [sort, setSort] = React.useState<BankAumSortState>({ key: null, order: null });
  const handleSort = React.useCallback(
    (key: BankAumSortKey) => setSort((current) => nextReportSort(current, key)),
    [],
  );

  return (
    <Collapsible open={open} onOpenChange={onOpenChange}>
      <Card
        className={cn(
          "overflow-hidden border-border/80 py-0 shadow-sm transition-shadow",
          open && "shadow-md ring-1 ring-primary/15",
        )}
      >
        <CollapsibleTrigger asChild>
          <button
            type="button"
            className="flex w-full items-center gap-4 px-4 py-4 text-left transition-colors hover:bg-muted/40"
          >
            <div
              className="flex size-10 shrink-0 items-center justify-center rounded-xl"
              style={{
                backgroundColor: `color-mix(in srgb, ${bank.accentColor} 18%, transparent)`,
                color: bank.accentColor,
              }}
            >
              <Building2 className="size-5" />
            </div>
            <div className="min-w-0 flex-1">
              <p className="font-semibold text-base leading-tight">{bank.bankName}</p>
              <p className="mt-0.5 text-muted-foreground text-xs">
                {bank.reportingCurrency}
                <span className="mx-1.5 text-border">·</span>
                {bank.assetClassRows.length} asset classes
                <span className="mx-1.5 text-border">·</span>
                {bank.assetTypeRows.length} types
                <span className="mx-1.5 text-border">·</span>
                {bank.sectorRows.length} sectors
                <span className="mx-1.5 text-border">·</span>
                {bank.geographyRows.length} countries
              </p>
            </div>
            <div className="hidden shrink-0 items-center gap-6 sm:flex">
              {compare ? (
                <>
                  <div className="text-right">
                    <p className="text-[10px] font-bold text-muted-foreground uppercase tracking-wide">{labelFrom}</p>
                    <p className="mt-0.5 text-sm tabular-nums">{formatMoneyOrDash(bank.d1Aum)}</p>
                  </div>
                  <div className="text-right">
                    <p className="text-[10px] font-bold text-muted-foreground uppercase tracking-wide">Change</p>
                    <p className={cn("mt-0.5 text-sm tabular-nums", pnlClass(diff))}>{formatDiff(diff)}</p>
                    <p className={cn("text-[11px] tabular-nums", pnlClass(diff))}>{diffPct}</p>
                  </div>
                </>
              ) : null}
              <div className="text-right">
                <p className="text-[10px] font-bold text-muted-foreground uppercase tracking-wide">
                  AUM ({bank.reportingCurrency})
                </p>
                <p className="mt-0.5 font-bold text-base tabular-nums">{formatMoneyOrDash(bank.d2Aum)}</p>
                <p className="text-muted-foreground text-[11px]">{pctOfTotal.toFixed(1)}% of total</p>
              </div>
              <div className="w-20">
                <p className="mb-1 text-right text-[10px] font-bold text-muted-foreground uppercase tracking-wide">Share</p>
                <div className="h-1.5 overflow-hidden rounded-full bg-muted">
                  <div
                    className="h-full rounded-full transition-all"
                    style={{ width: `${Math.min(pctOfTotal, 100)}%`, backgroundColor: bank.accentColor }}
                  />
                </div>
              </div>
            </div>
            <ChevronDown className={cn("size-5 shrink-0 text-muted-foreground transition-transform", open && "rotate-180")} />
          </button>
        </CollapsibleTrigger>
        <CollapsibleContent>
          <div className="border-t bg-muted/10 px-4 pb-4 pt-3">
            <Tabs defaultValue="assetClass">
              <TabsList className="h-9 w-full justify-start overflow-x-auto bg-background/80">
                {innerTabs.map((tab) => {
                  const count = tab.rows(bank).length;
                  return (
                    <TabsTrigger key={tab.id} value={tab.id} className="gap-1.5 text-xs">
                      {tab.icon}
                      {tab.label}
                      <Badge variant="secondary" className="h-5 min-w-5 px-1 font-normal text-[10px] tabular-nums">
                        {count}
                      </Badge>
                    </TabsTrigger>
                  );
                })}
              </TabsList>
              {innerTabs.map((tab) => (
                <TabsContent key={tab.id} value={tab.id} className="mt-3">
                  <BreakdownTable
                    rows={tab.rows(bank)}
                    compare={compare}
                    labelFrom={labelFrom}
                    labelTo={labelTo}
                    nameHeader={tab.nameHeader}
                    sort={sort}
                    onSort={handleSort}
                  />
                </TabsContent>
              ))}
            </Tabs>
          </div>
        </CollapsibleContent>
      </Card>
    </Collapsible>
  );
}

/** Bank-wise AUM reads the same per-date snapshot as the Overall Summary, via its own ACL-scoped routes. */
const BANK_WISE_AS_OF_CLIENT: AsOfSnapshotClient = {
  fetchStatus: fetchBankWiseAumAsOfStatusClient,
  kickoff: kickoffBankWiseAumAsOfClient,
};

function formatFilterDateLabel(value: string, fallback: string) {
  if (!value.trim()) return fallback;
  try {
    return format(parseISO(value), "dd MMM yyyy");
  } catch {
    return value;
  }
}

export function BankWiseAumView() {
  const params = useParams<{ tenant?: string }>();
  const tenant =
    typeof params?.tenant === "string" && params.tenant.trim()
      ? params.tenant.trim()
      : resolveCustomerTenant();
  const { layout, loading: layoutLoading } = useDefaultLayout(tenant);
  const layoutAcMode = presetForDefaultLayout(layout).bankWiseAumAcMode;
  const layoutAcModeRef = useLatestRef<BankWiseAumAssetClassMode>(layoutAcMode);

  const [filters, setFilters] = React.useState<BankWiseAumFilters>(defaultBankWiseAumFilters);
  const [settingsReady, setSettingsReady] = React.useState(false);
  const [openBanks, setOpenBanks] = React.useState<Record<string, boolean>>({});
  const [refreshKey, setRefreshKey] = React.useState(0);
  const { mode: headerAumMode } = useAumCalcMode();
  const skipFirstAumRefresh = React.useRef(true);
  const [layoutSeeded, setLayoutSeeded] = React.useState(false);

  // Seed the filters from the tenant layout once it has loaded (adjusted during render, not in an effect).
  useRenderEffect([layout, layoutLoading, layoutAcMode], () => {
    if (layoutLoading) return;
    if (!layoutSeeded) {
      setLayoutSeeded(true);
      setFilters((prev) => ({
        ...prev,
        acMode: isBankWiseAumAssetClassMode(layoutAcMode) ? layoutAcMode : "default",
        assetClass: "All",
      }));
    }
    setSettingsReady(true);
  });

  const resetFiltersForScope = React.useCallback(() => {
    setFilters({
      ...defaultBankWiseAumFilters,
      acMode: layoutAcModeRef.current,
    });
    setOpenBanks({});
    setRefreshKey((value) => value + 1);
  }, [layoutAcModeRef]);

  useImpersonationScopeRefresh(resetFiltersForScope);

  React.useEffect(() => {
    if (skipFirstAumRefresh.current) {
      skipFirstAumRefresh.current = false;
      return;
    }
    setRefreshKey((value) => value + 1);
  }, [headerAumMode]);

  useReportBankModeInvalidation(resetFiltersForScope);

  const { catalog: assetClassCatalog } = useAssetClassFilterCatalog(
    settingsReady ? filters.acMode : layoutAcMode,
    [],
    { enabled: !layoutLoading },
  );
  const listFilters = React.useMemo(() => {
    if (!filters.assetClass || filters.assetClass === "All") return filters;
    const labels = expandToUnderlierLabels(assetClassCatalog, [filters.assetClass]);
    return { ...filters, assetClass: labels.join("|") || "All" };
  }, [assetClassCatalog, filters]);

  // A past date reads that day's as-of snapshot (built in the background the first time it is
  // picked); today / a future date is live and needs none.
  const todayYmd = format(new Date(), "yyyy-MM-dd");
  const effectiveAsOf = filters.dateTo && filters.dateTo < todayYmd ? filters.dateTo : "";
  const handleAsOfBuilt = React.useCallback(() => {
    setRefreshKey((k) => k + 1);
    toast.success("Snapshot ready");
  }, []);
  const handleAsOfFailed = React.useCallback((error: string) => {
    toast.error(error);
  }, []);
  const {
    state: asOfState,
    isBuilding: isBuildingAsOf,
    errorMessage: asOfErrorMessage,
    rebuild: rebuildAsOf,
  } = useOverallSummaryAsOf({
    asOf: effectiveAsOf,
    onBuilt: handleAsOfBuilt,
    onFailed: handleAsOfFailed,
    client: BANK_WISE_AS_OF_CLIENT,
  });
  const asOfView = deriveAsOfViewState(effectiveAsOf, asOfState);
  const { snapshotReady: asOfSnapshotReady, waiting: asOfWaiting, failed: asOfFailed } = asOfView;

  const {
    banks: listedBanks,
    bankOptions,
    assetClassOptions,
    sectorOptions,
    countryOptions,
    label1: apiLabel1,
    label2: apiLabel2,
    reportingCurrency,
    aumMode,
    isLoading: listLoading,
    errorMessage,
  } = useBankWiseAumList(listFilters, refreshKey, settingsReady && asOfSnapshotReady);
  const isLoading = listLoading || asOfWaiting;
  // While a first-time snapshot builds, never show rows left over from another date.
  const banks = React.useMemo(
    () => (asOfWaiting || asOfFailed ? [] : listedBanks),
    [asOfWaiting, asOfFailed, listedBanks],
  );

  const handleRebuildSnapshot = async () => {
    try {
      await rebuildAsOf();
      toast.message("Snapshot rebuild queued", {
        description: `Rebuilding the summary as of ${formatYmd(effectiveAsOf)} in the background.`,
      });
    } catch (error) {
      toastApiError(error, "Could not start snapshot rebuild");
    }
  };

  const meta = bankWiseAumMeta;
  const effectiveFilters = React.useMemo(
    () => ({
      ...filters,
      banks: filters.banks.length > 0 ? filters.banks : bankOptions,
    }),
    [filters, bankOptions],
  );
  const filteredBanks = React.useMemo(
    () => sortBanksByAum(filterBanks(banks, effectiveFilters, bankOptions)),
    [banks, effectiveFilters, bankOptions],
  );
  const grand = React.useMemo(() => computeGrandTotals(filteredBanks), [filteredBanks]);
  const hasActiveFilters = hasActiveBankWiseAumFilters(effectiveFilters, bankOptions);
  const compare = isCompareMode(filters);

  const labelFrom = apiLabel1 || formatFilterDateLabel(filters.dateFrom, "Period 1");
  const labelTo = apiLabel2 || formatFilterDateLabel(filters.dateTo, "Current");
  const snapshotLabel = labelTo;

  const expandAll = () => {
    const next: Record<string, boolean> = {};
    for (const b of filteredBanks) next[b.id] = true;
    setOpenBanks(next);
  };

  const collapseAll = () => setOpenBanks({});

  const toggleBank = (id: string, open: boolean) => {
    setOpenBanks((p) => ({ ...p, [id]: open }));
  };

  return (
    <div className="flex min-w-0 flex-col gap-5">
      <div className="flex flex-col gap-3 border-b pb-4 lg:flex-row lg:items-start lg:justify-between">
        <div className="min-w-0 space-y-2">
          <div className="flex flex-wrap items-center gap-2.5">
            <div className="flex size-8 items-center justify-center rounded-lg border bg-sky-500/10 text-sky-700 dark:text-sky-400">
              <Building2 className="size-4" />
            </div>
            <h1 className="font-semibold text-2xl tracking-tight leading-none">{meta.title}</h1>
            <Badge variant="secondary" className="font-normal tabular-nums">
              {filteredBanks.length} banks
            </Badge>
            <Badge
              variant="outline"
              className={
                aumMode === "gross"
                  ? "border-sky-500/40 bg-sky-500/10 font-normal text-sky-700 dark:text-sky-400"
                  : "font-normal"
              }
            >
              AUM {aumMode === "gross" ? "Gross" : "Net"}
            </Badge>
            <Badge variant="outline" className="font-normal">
              {filters.acMode === "macro" ? "Macro" : filters.acMode === "bridge" ? "Bridge" : "Default"}
            </Badge>
          </div>
          <p className="text-muted-foreground text-sm">
            {meta.subtitle}
            {hasActiveFilters ? " · filtered" : ""}
          </p>
          {errorMessage ? <p className="text-destructive text-sm">{errorMessage}</p> : null}
          {asOfErrorMessage ? <p className="text-destructive text-sm">{asOfErrorMessage}</p> : null}
          {asOfFailed && asOfState?.error ? (
            <p className="text-destructive text-sm">Snapshot build failed: {asOfState.error}</p>
          ) : null}
        </div>
        <div className="flex shrink-0 items-center gap-2">
          <BankWiseAumFiltersDialog
            filters={effectiveFilters}
            bankOptions={bankOptions}
            assetClassOptions={assetClassOptions}
            sectorOptions={sectorOptions}
            countryOptions={countryOptions}
            onApply={setFilters}
          />
          <Button variant="outline" size="icon" className="size-9 shrink-0" onClick={() => window.print()}>
            <Printer className="size-4" />
            <span className="sr-only">Print</span>
          </Button>
          <DropdownMenu>
            <DropdownMenuTrigger asChild>
              <Button variant="outline" size="icon" className="size-9 shrink-0">
                <Download className="size-4" />
                <span className="sr-only">Download</span>
              </Button>
            </DropdownMenuTrigger>
            <DropdownMenuContent align="end">
              <DropdownMenuItem onClick={() => toast.message("Excel export", { description: "Coming soon." })}>
                Download Excel
              </DropdownMenuItem>
              <DropdownMenuItem onClick={() => toast.message("CSV export", { description: "Coming soon." })}>
                Download CSV
              </DropdownMenuItem>
            </DropdownMenuContent>
          </DropdownMenu>
          {effectiveAsOf ? (
            <Button
              variant="outline"
              size="sm"
              className="h-9 shrink-0 gap-1.5"
              disabled={isBuildingAsOf}
              onClick={() => void handleRebuildSnapshot()}
            >
              <RefreshCw className={cn("size-3.5", isBuildingAsOf && "animate-spin")} />
              {isBuildingAsOf ? "Rebuilding…" : "Rebuild snapshot"}
            </Button>
          ) : null}
          <Button
            variant="outline"
            size="icon"
            className="size-9 shrink-0"
            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="outline" size="icon" className="size-9 shrink-0" asChild>
            <Link href="/customer/reports">
              <X className="size-4" />
              <span className="sr-only">Close</span>
            </Link>
          </Button>
        </div>
      </div>

      {effectiveAsOf ? (
        <AsOfSnapshotBanner
          asOf={effectiveAsOf}
          view={asOfView}
          buildingAsOf={asOfState?.buildingAsOf}
          onBackToLive={() => setFilters((prev) => ({ ...prev, dateTo: "", dateFrom: "", compare: false }))}
        />
      ) : null}

      <div className="flex flex-wrap items-center gap-2 rounded-lg border border-sky-500/20 bg-sky-500/5 px-4 py-2.5 text-sm">
        <MapPin className="size-4 text-sky-600 dark:text-sky-400" />
        <span className="font-medium text-foreground">{formatPeriodLabel(filters)}</span>
        <span className="text-muted-foreground">·</span>
        <span className="text-muted-foreground">Generated {format(new Date(), "dd MMM yyyy, HH:mm")}</span>
        {compare ? (
          <>
            <span className="text-muted-foreground">·</span>
            <span className="inline-flex items-center gap-1 text-sky-700 text-xs dark:text-sky-400">
              <ArrowDownUp className="size-3.5" />
              Compare mode
            </span>
          </>
        ) : (
          <>
            <span className="text-muted-foreground">·</span>
            <span className="text-sky-700 text-xs dark:text-sky-400">Snapshot view</span>
          </>
        )}
      </div>

      <div className="overflow-hidden rounded-xl bg-gradient-to-br from-slate-800 via-slate-700 to-slate-800 text-white shadow-lg">
        <div className="flex flex-wrap items-center gap-4 px-5 py-4">
          <div>
            <p className="text-[11px] font-bold text-white/60 uppercase tracking-wider">Total AUM</p>
            <p className="mt-1 font-bold text-2xl tabular-nums tracking-tight">{formatMoneyOrDash(grand.d2Aum)}</p>
            <p className="mt-0.5 text-sm text-white/75">
              {grand.bankCount} banks · {reportingCurrency}{" "}
              equivalent · {aumMode === "gross" ? "Gross" : "Net"}
            </p>
          </div>
          {compare ? (
            <>
              <div className="hidden h-12 w-px bg-white/15 sm:block" />
              <div>
                <p className="text-[11px] font-bold text-white/60 uppercase tracking-wider">{labelFrom}</p>
                <p className="mt-1 font-semibold text-lg tabular-nums">{formatMoneyOrDash(grand.d1Aum)}</p>
              </div>
              <div className="hidden h-12 w-px bg-white/15 sm:block" />
              <div>
                <p className="text-[11px] font-bold text-white/60 uppercase tracking-wider">Net change</p>
                <p className={cn("mt-1 font-semibold text-lg tabular-nums", grand.diff >= 0 ? "text-emerald-300" : "text-red-300")}>
                  {formatDiff(grand.diff)}
                </p>
                <p className={cn("text-sm tabular-nums", grand.diff >= 0 ? "text-emerald-300/90" : "text-red-300/90")}>
                  {grand.diffPct === null ? "New" : formatDiffPct(grand.d1Aum, grand.d2Aum)}
                </p>
              </div>
            </>
          ) : (
            <>
              <div className="hidden h-12 w-px bg-white/15 sm:block" />
              <div>
                <p className="text-[11px] font-bold text-white/60 uppercase tracking-wider">Purchase value</p>
                <p className="mt-1 font-semibold text-lg tabular-nums">{formatMoneyOrDash(grand.d2Pv)}</p>
              </div>
            </>
          )}
          <div className="ml-auto flex gap-2 print:hidden">
            <Button
              type="button"
              variant="secondary"
              size="sm"
              className="h-8 border-white/20 bg-white/10 text-white hover:bg-white/20"
              onClick={expandAll}
            >
              <ChevronsDownUp className="size-3.5" />
              Expand all
            </Button>
            <Button
              type="button"
              variant="secondary"
              size="sm"
              className="h-8 border-white/20 bg-white/10 text-white hover:bg-white/20"
              onClick={collapseAll}
            >
              Collapse all
            </Button>
          </div>
        </div>
      </div>

      <ReportLoadingPanel
        loading={isLoading}
        label="Loading bank-wise AUM…"
        className={cn(isLoading && "pointer-events-none")}
      >
        {filteredBanks.length === 0 ? (
          !isLoading ? (
            <Card className="border-dashed">
              <CardContent className="py-16 text-center text-muted-foreground">
                <Building2 className="mx-auto mb-3 size-10 opacity-40" />
                <p className="font-medium">No data found</p>
                <p className="mt-1 text-sm">Adjust filters or date range and try again.</p>
              </CardContent>
            </Card>
          ) : (
            <div className="h-full min-h-[calc(100vh-280px)]" aria-hidden />
          )
        ) : (
          <div className="flex flex-col gap-3">
          {filteredBanks.map((bank) => (
            <BankAccordion
              key={bank.id}
              bank={bank}
              compare={compare}
              labelFrom={labelFrom}
              labelTo={compare ? labelTo : snapshotLabel}
              totalAum={grand.d2Aum}
              open={!!openBanks[bank.id]}
              onOpenChange={(open) => toggleBank(bank.id, open)}
            />
          ))}
        </div>
        )}
      </ReportLoadingPanel>
    </div>
  );
}
