"use client"

import * as React from "react"
import { format } from "date-fns"
import { ClipboardList, FileSpreadsheet, RefreshCw, X } from "lucide-react"
import { useParams } from "next/navigation"
import { toast } from "sonner"

import { useCustomerPortalSession } from "@/app/customer/_components/customer-portal-session-context"
import { useAumCalcMode } from "@/app/customer/_lib/use-aum-calc-mode"
import { resolveCustomerTenant } from "@/app/customer/_lib/resolve-customer-tenant"
import { useReportBankModeInvalidation } from "@/app/customer/_lib/use-report-bank-mode-invalidation";
import { AsOfSnapshotBanner } from "@/app/customer/[tenant]/reports/_shared/components/as-of-snapshot-banner"
import { ReportLifecycleBadge } from "@/app/customer/[tenant]/reports/_shared/components/report-lifecycle-badge"
import { ReportLoadingPanel } from "@/app/customer/[tenant]/reports/_shared/components/report-loading-panel"
import { deriveAsOfViewState, formatYmd } from "@/app/customer/[tenant]/reports/_shared/lib/as-of-snapshot-view"
import { DatePicker } from "@/components/date-range-picker"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { isCorporateParentPortalUser } from "@/lib/frontend-auth/report-capabilities"
import { toastApiError } from "@/lib/toast-api-error"
import { cn } from "@/lib/utils"

import { formatAccounting, formatPlainAmount } from "@/lib/format/numbers";
import { useOverallSummaryAsOf, type AsOfSnapshotClient } from "../../overall-summary/_lib/use-overall-summary-asof"
import { fetchPibPivotAsOfStatusClient, kickoffPibPivotAsOfClient } from "../_lib/pib-pivot-asof-api"
import { CUSTOMER_ROUTE_KEYS } from "@/config/customer-route-keys";
import { sessionAllowsRouteKeys } from "@/app/customer/_lib/customer-route-guard";
import { usePibPivotReport } from "../_lib/use-pib-pivot-report"
import {
  PIB_PIVOT_ASSET_COLUMNS,
  PIB_PIVOT_LOAN_BUCKET,
  PIB_WORKBOOK_TABS,
  isSingleClientPivot,
  pivotAssetTotal,
  pivotInferredValue,
  pivotPotentialCount,
  pivotPotentialValue,
  pivotUnmappedCount,
  type PibPivotAssetBucketKey,
  type PibPivotBucketKey,
  type PibPivotRow,
  type PibWorkbookTabId,
} from "../_lib/pib-pivot-data"
import { PibIndividualOverview } from "./pib-individual-overview"
import {
  PibPivotDetailDialog,
  type PibPivotDetailTarget,
} from "./pib-pivot-detail-dialog"
import { PibSummaryCalcHelp } from "./pib-summary-calc-help"



function aumClass(value: number | null | undefined): string {
  if (value == null) return "text-muted-foreground/40"
  if (value < 0) return "text-red-600 dark:text-red-400"
  if (value === 0) return "text-muted-foreground/50"
  return "tabular-nums text-foreground"
}

/** Keep amount columns from collapsing so whitespace-nowrap values do not bleed left. */
/** PIB grids follow accounting convention: negatives in parentheses, blank when absent. */
function formatPibValue(value: number | null | undefined, empty = ""): string {
  return formatAccounting(value, empty)
}

const PIVOT_AMOUNT_COL_MIN_WIDTH = 108

function PivotHeaderCell({
  code,
  label,
  className,
}: {
  code?: string
  label: string
  className?: string
}) {
  return (
    <th
      title={code ? `${code} — ${label}` : label}
      style={{
        top: 0,
        height: PIVOT_LABEL_HEADER_HEIGHT,
        minWidth: PIVOT_AMOUNT_COL_MIN_WIDTH,
        width: PIVOT_AMOUNT_COL_MIN_WIDTH,
      }}
      className={cn(
        "sticky top-0 z-10 border-b bg-muted px-2 py-0 align-bottom font-medium text-xs whitespace-nowrap text-foreground",
        className
      )}
    >
      <div className="flex h-full flex-col justify-end gap-0.5 pb-1">
        <span className="leading-tight">{label}</span>
        {code ? (
          <span className="font-normal font-mono text-[10px] leading-none text-muted-foreground">
            {code}
          </span>
        ) : (
          <span className="h-2.5" aria-hidden />
        )}
      </div>
    </th>
  )
}

const PIVOT_STICKY_COLS = [
  { key: "residency", label: "Residency", width: 110 },
  { key: "rm", label: "RM", width: 52 },
  { key: "acType", label: "Ac Type", width: 64 },
  { key: "jurisdiction", label: "C.Nation", width: 72 },
  { key: "bank", label: "Bank", width: 128 },
  { key: "bankJurisdiction", label: "Bank Jur.", width: 84 },
  { key: "client", label: "Client", width: 180 },
] as const

type PivotDimKey = (typeof PIVOT_STICKY_COLS)[number]["key"]
type PivotColumnFilters = Record<PivotDimKey, string>

const EMPTY_PIVOT_COLUMN_FILTERS: PivotColumnFilters = {
  residency: "",
  rm: "",
  acType: "",
  jurisdiction: "",
  bank: "",
  bankJurisdiction: "",
  client: "",
}

/** Must match rendered label-header row height (incl. B410 code line). */
const PIVOT_LABEL_HEADER_HEIGHT = 40
const PIVOT_FILTER_HEADER_TOP = PIVOT_LABEL_HEADER_HEIGHT

function pivotDimValues(row: PibPivotRow): readonly string[] {
  return [row.residency, row.rm, row.acType, row.jurisdiction, row.bank, row.bankJurisdiction, row.client]
}

function stickyLeft(index: number): number {
  return PIVOT_STICKY_COLS.slice(0, index).reduce((sum, col) => sum + col.width, 0)
}

function customerGroupKey(row: PibPivotRow): string {
  const id = row.customerId
  if (id != null && id > 0) {
    return `id:${id}`
  }
  const name = row.client.trim().toLowerCase()
  return name !== "" ? `name:${name}` : `row:${row.bank}|${row.rm}|${row.acType}`
}

/** Contiguous same-customer runs (rows are already sorted by client). */
function buildCustomerGroupMeta(rows: PibPivotRow[]) {
  const size = new Array<number>(rows.length).fill(1)
  const indexInGroup = new Array<number>(rows.length).fill(0)

  let i = 0
  while (i < rows.length) {
    const key = customerGroupKey(rows[i])
    let j = i + 1
    while (j < rows.length && customerGroupKey(rows[j]) === key) {
      j++
    }
    const groupSize = j - i
    for (let k = i; k < j; k++) {
      size[k] = groupSize
      indexInGroup[k] = k - i
    }
    i = j
  }

  return { size, indexInGroup }
}

function nAmount(value: number | null | undefined): number {
  return value == null || !Number.isFinite(value) ? 0 : value
}

function sumPivotColumnTotals(rows: PibPivotRow[]) {
  const assets = Object.fromEntries(
    PIB_PIVOT_ASSET_COLUMNS.map((c) => [c.key, 0])
  ) as Record<(typeof PIB_PIVOT_ASSET_COLUMNS)[number]["key"], number>

  let total = 0
  let loanLiability = 0
  let pickUp = 0

  for (const row of rows) {
    for (const col of PIB_PIVOT_ASSET_COLUMNS) {
      assets[col.key] += nAmount(row[col.key] as number | null)
    }
    total += pivotAssetTotal(row)
    loanLiability += nAmount(row.loanLiability)
    pickUp += nAmount(row.pickUpTotalFromSumm)
  }

  return { assets, total, loanLiability, pickUp }
}

function isDrillableAmount(value: number | null | undefined): value is number {
  return value != null && Number.isFinite(value) && value !== 0
}

function hasActivePivotColumnFilters(filters: PivotColumnFilters): boolean {
  return PIVOT_STICKY_COLS.some((col) => filters[col.key].trim() !== "")
}

function filterPivotRows(rows: PibPivotRow[], filters: PivotColumnFilters): PibPivotRow[] {
  if (!hasActivePivotColumnFilters(filters)) {
    return rows
  }

  return rows.filter((row) =>
    PIVOT_STICKY_COLS.every((col) => {
      const query = filters[col.key].trim().toLowerCase()
      if (!query) return true
      return String(row[col.key] ?? "")
        .toLowerCase()
        .includes(query)
    })
  )
}

function pivotHasMeta(rows: PibPivotRow[]): {
  pickUp: boolean
  error: boolean
  classification: boolean
  flags: boolean
} {
  let pickUp = false
  let error = false
  let classification = false
  let flags = false
  for (const row of rows) {
    if (row.pickUpTotalFromSumm != null && row.pickUpTotalFromSumm !== 0) pickUp = true
    if (row.error != null) error = true
    if (row.classification.trim() !== "") classification = true
    const f = row.flags
    if (f.aa || f.kc || f.dr || f.in || f.tm) flags = true
    if (pickUp && error && classification && flags) break
  }
  return { pickUp, error, classification, flags }
}

function PivotAmountCell({
  value,
  unmappedCount,
  inferredValue,
  potentialCount,
  potentialValue,
  canDrill,
  drillTitle,
  onDrill,
}: {
  value: number | null
  unmappedCount: number
  inferredValue: number
  potentialCount: number
  potentialValue: number
  canDrill: boolean
  drillTitle?: string
  onDrill?: () => void
}) {
  const hasUnmapped = unmappedCount > 0
  const hasPotential = potentialCount > 0
  const cellTotal = value ?? 0
  const unmappedTitle = hasUnmapped
    ? unmappedCount === 1
      ? `1 holding inferred (${formatPibValue(inferredValue)} of ${formatPibValue(cellTotal)})`
      : `${unmappedCount} holdings inferred (${formatPibValue(inferredValue)} of ${formatPibValue(cellTotal)})`
    : undefined
  const potentialTitle = hasPotential
    ? potentialCount === 1
      ? `1 potential note (${formatPibValue(potentialValue)}) — shown on drill, not in PIB totals`
      : `${potentialCount} potential notes (${formatPibValue(potentialValue)}) — shown on drill, not in PIB totals`
    : undefined
  const combinedTitle = [unmappedTitle, potentialTitle].filter(Boolean).join(" · ") || undefined

  return (
    <div className="flex items-center justify-end gap-1">
      {hasUnmapped ? (
        <span
          className="size-1.5 shrink-0 rounded-full bg-amber-500 ring-2 ring-amber-500/25"
          title={unmappedTitle}
          aria-label={unmappedTitle}
        />
      ) : null}
      {hasPotential ? (
        <ReportLifecycleBadge
          kind="potential"
          title={potentialTitle}
          className="shrink-0"
        />
      ) : null}
      {canDrill && onDrill ? (
        <button
          type="button"
          className={cn(
            "text-right underline-offset-2 hover:text-primary hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
            hasUnmapped && "font-medium text-amber-900 dark:text-amber-200"
          )}
          title={drillTitle ?? combinedTitle}
          onClick={onDrill}
        >
          {formatPibValue(value)}
        </button>
      ) : (
        <span
          className={cn(hasUnmapped && "font-medium text-amber-900 dark:text-amber-200")}
          title={combinedTitle}
        >
          {formatPibValue(value)}
        </span>
      )}
    </div>
  )
}

function PivotSheet({
  rows,
  onCellClick,
  onFilterStatsChange,
}: {
  rows: PibPivotRow[]
  onCellClick?: (target: PibPivotDetailTarget) => void
  onFilterStatsChange?: (stats: { visible: number; total: number; active: boolean }) => void
}) {
  const [columnFilters, setColumnFilters] = React.useState<PivotColumnFilters>(EMPTY_PIVOT_COLUMN_FILTERS)
  const visibleRows = React.useMemo(
    () => filterPivotRows(rows, columnFilters),
    [rows, columnFilters]
  )
  const columnTotals = React.useMemo(() => sumPivotColumnTotals(visibleRows), [visibleRows])
  const customerGroups = React.useMemo(
    () => buildCustomerGroupMeta(visibleRows),
    [visibleRows]
  )
  const filtersActive = hasActivePivotColumnFilters(columnFilters)
  const meta = pivotHasMeta(rows)

  React.useEffect(() => {
    onFilterStatsChange?.({
      visible: visibleRows.length,
      total: rows.length,
      active: filtersActive,
    })
  }, [visibleRows.length, rows.length, filtersActive, onFilterStatsChange])

  const setCol = (key: PivotDimKey, value: string) => {
    setColumnFilters((prev) => ({ ...prev, [key]: value }))
  }

  const clearFilters = () => setColumnFilters(EMPTY_PIVOT_COLUMN_FILTERS)

  const openDetail = (
    row: PibPivotRow,
    bucket: PibPivotBucketKey,
    bucketLabel: string,
    cellValue: number
  ) => {
    const customerId = row.customerId ?? 0
    if (!onCellClick || customerId <= 0 || row.bank.trim() === "") {
      return
    }
    onCellClick({
      bucket,
      bucketLabel,
      customerId,
      bank: row.bank,
      client: row.client,
      cellValue,
    })
  }

  const cellPad = "px-2 py-1.5"
  const trailingBlankCount =
    PIB_PIVOT_ASSET_COLUMNS.length +
    2 +
    (meta.pickUp ? 1 : 0) +
    (meta.error ? 1 : 0) +
    (meta.classification ? 1 : 0) +
    (meta.flags ? 5 : 0)

  return (
    <div className="min-h-0 flex-1 overflow-auto">
      <table className="w-max min-w-full caption-bottom border-collapse text-sm leading-snug">
        <thead>
          <tr>
            {PIVOT_STICKY_COLS.map((col, i) => {
              const isLast = i === PIVOT_STICKY_COLS.length - 1
              return (
                <th
                  key={col.key}
                  style={{
                    left: stickyLeft(i),
                    width: col.width,
                    minWidth: col.width,
                    maxWidth: col.width,
                    top: 0,
                    height: PIVOT_LABEL_HEADER_HEIGHT,
                  }}
                  className={cn(
                    "sticky z-40 border-b bg-muted px-2 py-0 text-left align-bottom font-medium text-xs whitespace-nowrap",
                    isLast && "border-r shadow-[2px_0_4px_-2px_rgba(0,0,0,0.15)]"
                  )}
                >
                  <div className="flex h-full flex-col justify-end pb-1">
                    <span className="leading-tight">{col.label}</span>
                    <span className="h-2.5" aria-hidden />
                  </div>
                </th>
              )
            })}
            {PIB_PIVOT_ASSET_COLUMNS.map((c) => (
              <PivotHeaderCell
                key={`${c.code}-${c.shortLabel}`}
                code={c.code}
                label={c.compactLabel}
                className="z-10 min-w-[6.75rem] text-right"
              />
            ))}
            <PivotHeaderCell label="TOTAL" className="z-10 min-w-[6.75rem] text-right font-semibold" />
            <PivotHeaderCell label="Loan / Overdraft" className="z-10 min-w-[6.75rem] text-right" />
            {meta.pickUp ? (
              <PivotHeaderCell label="Pick up" className="z-10 min-w-[5.5rem] text-right" />
            ) : null}
            {meta.error ? (
              <PivotHeaderCell label="Error" className="z-10 min-w-[4.5rem] text-right" />
            ) : null}
            {meta.classification ? (
              <PivotHeaderCell label="Classification" className="z-10 min-w-[7rem]" />
            ) : null}
            {meta.flags
              ? (["AA", "KC", "DR", "IN", "TM"] as const).map((flag) => (
                  <th
                    key={flag}
                    style={{ top: 0, height: PIVOT_LABEL_HEADER_HEIGHT }}
                    className="sticky z-30 w-8 border-b bg-muted px-1 py-0 text-center align-bottom font-medium text-[10px] whitespace-nowrap"
                  >
                    <div className="flex h-full flex-col justify-end pb-1">
                      <span className="leading-tight">{flag}</span>
                      <span className="h-2.5" aria-hidden />
                    </div>
                  </th>
                ))
              : null}
          </tr>
          <tr className="bg-muted/40">
            {PIVOT_STICKY_COLS.map((col, i) => {
              const isLast = i === PIVOT_STICKY_COLS.length - 1
              return (
                <th
                  key={`filter-${col.key}`}
                  style={{
                    left: stickyLeft(i),
                    width: col.width,
                    minWidth: col.width,
                    maxWidth: col.width,
                    top: PIVOT_FILTER_HEADER_TOP,
                  }}
                  className={cn(
                    "sticky z-40 border-b bg-muted p-0.5 align-middle font-normal",
                    isLast && "border-r shadow-[2px_0_4px_-2px_rgba(0,0,0,0.15)]"
                  )}
                >
                  <Input
                    value={columnFilters[col.key]}
                    onChange={(e) => setCol(col.key, e.target.value)}
                    placeholder="Filter…"
                    aria-label={`Filter ${col.label}`}
                    className="h-7 min-w-0 rounded-md border-border/70 bg-background px-1.5 text-xs font-normal shadow-none placeholder:font-normal placeholder:text-muted-foreground/60"
                  />
                </th>
              )
            })}
            {/* One cell per trailing column (same count/widths as header+body).
                A single colSpan "Clear filters" cell used to stretch the grid. */}
            {Array.from({ length: trailingBlankCount }, (_, i) => {
              const flagStart =
                trailingBlankCount - (meta.flags ? 5 : 0)
              const isFlagCol = meta.flags && i >= flagStart
              return (
                <th
                  key={`filter-amt-${i}`}
                  style={{
                    top: PIVOT_FILTER_HEADER_TOP,
                    ...(isFlagCol
                      ? { width: 32, minWidth: 32, maxWidth: 32 }
                      : {
                          minWidth: PIVOT_AMOUNT_COL_MIN_WIDTH,
                          width: PIVOT_AMOUNT_COL_MIN_WIDTH,
                        }),
                  }}
                  className={cn(
                    "sticky z-30 border-b bg-muted py-0.5 align-middle font-normal",
                    isFlagCol ? "w-8 px-0.5" : "px-1 text-left"
                  )}
                >
                  {i === 0 ? (
                    filtersActive ? (
                      <button
                        type="button"
                        onClick={clearFilters}
                        title={`Clear filters (${visibleRows.length}/${rows.length})`}
                        className="inline-flex h-7 max-w-full items-center gap-1 truncate rounded-md px-1.5 text-[11px] font-medium text-muted-foreground hover:bg-background hover:text-foreground"
                      >
                        <X className="size-3.5 shrink-0" />
                        <span className="truncate">
                          Clear ({visibleRows.length}/{rows.length})
                        </span>
                      </button>
                    ) : (
                      <span className="px-1 text-[11px] text-muted-foreground">Filter…</span>
                    )
                  ) : null}
                </th>
              )
            })}
          </tr>
        </thead>
        <tbody>
          {visibleRows.length === 0 ? (
            <tr>
              <td
                colSpan={PIVOT_STICKY_COLS.length + trailingBlankCount}
                className="px-4 py-8 text-center text-muted-foreground text-xs"
              >
                {filtersActive ? "No pivot rows match these column filters." : "No pivot data."}
              </td>
            </tr>
          ) : (
            visibleRows.map((row, rowIndex) => {
              const total = pivotAssetTotal(row)
              const dimValues = pivotDimValues(row)
              const groupSize = customerGroups.size[rowIndex] ?? 1
              const groupIndex = customerGroups.indexInGroup[rowIndex] ?? 0
              const isMultiCustomer = groupSize >= 2
              const isGroupStart = isMultiCustomer && groupIndex === 0
              const isGroupCont = isMultiCustomer && groupIndex > 0

              return (
                <tr
                  key={`${row.customerId ?? "x"}-${row.bank}-${row.client}-${row.rm}-${rowIndex}`}
                  className={cn(
                    "border-b border-border/50 last:border-0",
                    isGroupStart && "border-t border-t-border/80",
                    isMultiCustomer && "bg-muted/[0.18]"
                  )}
                >
                  {PIVOT_STICKY_COLS.map((col, i) => {
                    const isLast = i === PIVOT_STICKY_COLS.length - 1
                    const isClient = col.key === "client"
                    // Every row carries its own Residency / RM / Ac Type / Jurisdiction / Bank / Client —
                    // blanking repeats made later rows of a client look like missing data.
                    const text = dimValues[i]
                    return (
                      <td
                        key={col.key}
                        style={{
                          left: stickyLeft(i),
                          width: col.width,
                          minWidth: col.width,
                          maxWidth: col.width,
                        }}
                        title={
                          col.key === "rm"
                            ? row.rmName?.trim() || text || undefined
                            : isClient && isGroupCont
                              ? row.client || undefined
                              : text || undefined
                        }
                        className={cn(
                          "sticky z-30 overflow-hidden text-ellipsis bg-background px-2 py-1.5 text-xs whitespace-nowrap",
                          isMultiCustomer && "bg-muted",
                          isClient && isMultiCustomer && "border-l-2 border-l-emerald-600/50",
                          col.key === "bank" && "font-medium",
                          isClient &&
                            row.clientHighlight &&
                            "bg-amber-100 font-medium dark:bg-amber-950",
                          isClient && "font-medium",
                          isLast && "border-r shadow-[2px_0_4px_-2px_rgba(0,0,0,0.12)]"
                        )}
                      >
                        {text}
                      </td>
                    )
                  })}
                  {PIB_PIVOT_ASSET_COLUMNS.map((c) => {
                    const v = row[c.key] as number | null
                    const bucketKey = c.key as PibPivotAssetBucketKey
                    const unmappedCount = pivotUnmappedCount(row, bucketKey)
                    const inferredValue = pivotInferredValue(row, bucketKey)
                    const potentialCount = pivotPotentialCount(row, bucketKey)
                    const potentialValue = pivotPotentialValue(row, bucketKey)
                    const canDrill =
                      onCellClick &&
                      (row.customerId ?? 0) > 0 &&
                      isDrillableAmount(v)
                    return (
                      <td
                        key={`${c.code}-${c.shortLabel}`}
                        style={{
                          minWidth: PIVOT_AMOUNT_COL_MIN_WIDTH,
                          width: PIVOT_AMOUNT_COL_MIN_WIDTH,
                        }}
                        className={cn(
                          cellPad,
                          "relative z-0 text-right whitespace-nowrap",
                          aumClass(v),
                          unmappedCount > 0 && "bg-amber-50 dark:bg-amber-950/40",
                          unmappedCount === 0 &&
                            potentialCount > 0 &&
                            "bg-amber-500/5 dark:bg-amber-500/10"
                        )}
                      >
                        <PivotAmountCell
                          value={v}
                          unmappedCount={unmappedCount}
                          inferredValue={inferredValue}
                          potentialCount={potentialCount}
                          potentialValue={potentialValue}
                          canDrill={Boolean(canDrill)}
                          drillTitle={`View ${c.shortLabel} holdings`}
                          onDrill={
                            canDrill
                              ? () => openDetail(row, c.key, c.shortLabel, v)
                              : undefined
                          }
                        />
                      </td>
                    )
                  })}
                  <td
                    className={cn(
                      cellPad,
                      "relative z-0 text-right font-semibold tabular-nums whitespace-nowrap"
                    )}
                    style={{
                      minWidth: PIVOT_AMOUNT_COL_MIN_WIDTH,
                      width: PIVOT_AMOUNT_COL_MIN_WIDTH,
                    }}
                  >
                    {formatPibValue(total)}
                  </td>
                  <td
                    className={cn(
                      cellPad,
                      "relative z-0 text-right whitespace-nowrap",
                      aumClass(row.loanLiability)
                    )}
                    style={{
                      minWidth: PIVOT_AMOUNT_COL_MIN_WIDTH,
                      width: PIVOT_AMOUNT_COL_MIN_WIDTH,
                    }}
                  >
                    {onCellClick &&
                    (row.customerId ?? 0) > 0 &&
                    isDrillableAmount(row.loanLiability) ? (
                      <button
                        type="button"
                        className="w-full text-right underline-offset-2 hover:text-primary hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
                        title="View Loan / Overdraft holdings"
                        onClick={() =>
                          openDetail(
                            row,
                            PIB_PIVOT_LOAN_BUCKET,
                            "Loan / Overdraft",
                            row.loanLiability as number
                          )
                        }
                      >
                        {formatPibValue(row.loanLiability)}
                      </button>
                    ) : (
                      formatPibValue(row.loanLiability)
                    )}
                  </td>
                  {meta.pickUp ? (
                    <td
                      className={cn(cellPad, "text-right tabular-nums whitespace-nowrap")}
                      style={{
                        minWidth: PIVOT_AMOUNT_COL_MIN_WIDTH,
                        width: PIVOT_AMOUNT_COL_MIN_WIDTH,
                      }}
                    >
                      {formatPibValue(row.pickUpTotalFromSumm)}
                    </td>
                  ) : null}
                  {meta.error ? (
                    <td
                      className={cn(
                        cellPad,
                        "text-right tabular-nums text-muted-foreground whitespace-nowrap"
                      )}
                      style={{
                        minWidth: PIVOT_AMOUNT_COL_MIN_WIDTH,
                        width: PIVOT_AMOUNT_COL_MIN_WIDTH,
                      }}
                    >
                      {row.error == null ? "" : String(row.error)}
                    </td>
                  ) : null}
                  {meta.classification ? (
                    <td
                      className={cn(cellPad, "truncate whitespace-nowrap")}
                      style={{
                        minWidth: PIVOT_AMOUNT_COL_MIN_WIDTH,
                        width: PIVOT_AMOUNT_COL_MIN_WIDTH,
                        maxWidth: PIVOT_AMOUNT_COL_MIN_WIDTH,
                      }}
                      title={row.classification || undefined}
                    >
                      {row.classification}
                    </td>
                  ) : null}
                  {meta.flags ? (
                    <>
                      <td className="px-0.5 py-0.5 text-center text-[10px] text-muted-foreground">
                        {row.flags.aa}
                      </td>
                      <td className="px-0.5 py-0.5 text-center text-[10px] text-muted-foreground">
                        {row.flags.kc}
                      </td>
                      <td className="px-0.5 py-0.5 text-center text-[10px] text-muted-foreground">
                        {row.flags.dr}
                      </td>
                      <td className="px-0.5 py-0.5 text-center text-[10px] text-muted-foreground">
                        {row.flags.in}
                      </td>
                      <td className="px-0.5 py-0.5 text-center text-[10px] text-muted-foreground">
                        {row.flags.tm}
                      </td>
                    </>
                  ) : null}
                </tr>
              )
            })
          )}
        </tbody>
        {visibleRows.length > 0 ? (
          <tfoot>
            <tr className="border-t-2 border-border">
              {PIVOT_STICKY_COLS.map((col, i) => {
                const isLast = i === PIVOT_STICKY_COLS.length - 1
                const isFirst = i === 0
                return (
                  <td
                    key={`total-${col.key}`}
                    style={{
                      left: stickyLeft(i),
                      width: col.width,
                      minWidth: col.width,
                      maxWidth: col.width,
                      bottom: 0,
                    }}
                    className={cn(
                      "sticky z-30 bg-muted px-2 py-1.5 text-xs font-semibold whitespace-nowrap",
                      isLast && "border-r shadow-[2px_0_4px_-2px_rgba(0,0,0,0.12)]"
                    )}
                  >
                    {isFirst ? "Total" : ""}
                  </td>
                )
              })}
              {PIB_PIVOT_ASSET_COLUMNS.map((c) => {
                const v = columnTotals.assets[c.key]
                return (
                  <td
                    key={`total-${c.key}`}
                    className={cn(
                      cellPad,
                      "sticky bottom-0 z-20 bg-muted text-right font-semibold tabular-nums whitespace-nowrap",
                      aumClass(v)
                    )}
                  >
                    {formatPibValue(v)}
                  </td>
                )
              })}
              <td
                className={cn(
                  cellPad,
                  "sticky bottom-0 z-20 bg-muted text-right font-semibold tabular-nums whitespace-nowrap"
                )}
              >
                {formatPibValue(columnTotals.total)}
              </td>
              <td
                className={cn(
                  cellPad,
                  "sticky bottom-0 z-20 bg-muted text-right font-semibold tabular-nums whitespace-nowrap",
                  aumClass(columnTotals.loanLiability)
                )}
              >
                {formatPibValue(columnTotals.loanLiability)}
              </td>
              {meta.pickUp ? (
                <td
                  className={cn(
                    cellPad,
                    "sticky bottom-0 z-20 bg-muted text-right font-semibold tabular-nums whitespace-nowrap"
                  )}
                >
                  {formatPibValue(columnTotals.pickUp)}
                </td>
              ) : null}
              {meta.error ? (
                <td className="sticky bottom-0 z-20 bg-muted px-2 py-1.5" />
              ) : null}
              {meta.classification ? (
                <td className="sticky bottom-0 z-20 bg-muted px-2 py-1.5" />
              ) : null}
              {meta.flags
                ? (["aa", "kc", "dr", "in", "tm"] as const).map((flag) => (
                    <td key={`total-flag-${flag}`} className="sticky bottom-0 z-20 bg-muted px-0.5 py-0.5" />
                  ))
                : null}
            </tr>
          </tfoot>
        ) : null}
      </table>
    </div>
  )
}

function PlaceholderSheet({ label }: { label: string }) {
  return (
    <div className="flex min-h-[20rem] flex-1 flex-col items-center justify-center gap-2 px-6 text-center">
      <FileSpreadsheet className="size-10 text-muted-foreground/40" />
      <p className="font-medium text-sm">{label}</p>
      <p className="max-w-sm text-muted-foreground text-xs">
        Empty for now — only the Pivot sheet is live.
      </p>
    </div>
  )
}

function WorkbookTabs({
  active,
  onChange,
}: {
  active: PibWorkbookTabId
  onChange: (id: PibWorkbookTabId) => void
}) {
  const readyTabs = PIB_WORKBOOK_TABS.filter((tab) => tab.ready)
  // Only Pivot is live — skip the sheet strip until more tabs ship.
  if (readyTabs.length <= 1) {
    return null
  }

  return (
    <div className="shrink-0 border-t bg-muted/30">
      <div
        className="flex items-end gap-0.5 overflow-x-auto px-2 pt-1.5 pb-0"
        role="tablist"
        aria-label="Workbook sheets"
      >
        {readyTabs.map((tab) => {
          const isActive = tab.id === active
          return (
            <button
              key={tab.id}
              type="button"
              role="tab"
              aria-selected={isActive}
              onClick={() => onChange(tab.id)}
              className={cn(
                "relative -mb-px shrink-0 truncate rounded-t-md border px-3 py-1.5 text-left text-xs transition-colors",
                isActive
                  ? "z-10 border-b-background border-border bg-background font-medium text-foreground shadow-sm"
                  : "border-transparent text-muted-foreground hover:bg-muted/80 hover:text-foreground"
              )}
              title={tab.label}
            >
              {tab.label}
            </button>
          )
        })}
      </div>
    </div>
  )
}

/** The PIB pivot reads the same per-date snapshot as the Overall Summary, via its own ACL-scoped routes. */
const PIB_AS_OF_CLIENT: AsOfSnapshotClient = {
  fetchStatus: fetchPibPivotAsOfStatusClient,
  kickoff: kickoffPibPivotAsOfClient,
}

export function PibReportView({ allowedRoutes = [] }: { allowedRoutes?: readonly string[] }) {
  const params = useParams<{ tenant?: string }>()
  const tenant =
    typeof params?.tenant === "string" && params.tenant.trim()
      ? params.tenant.trim()
      : resolveCustomerTenant()
  // Corporate parents get the pivot grid; an individual login has a single client, so it gets
  // the calculation + holdings overview instead.
  const { user } = useCustomerPortalSession()
  const isIndividualLogin = !isCorporateParentPortalUser(user)
  const [refreshKey, setRefreshKey] = React.useState(0)
  // The total follows the AUM mode (Gross / Net) like the Overall Summary, so a change in the header
  // badge must re-read the report. The first value is the initial load, not a change.
  const { mode: headerAumMode } = useAumCalcMode()
  const skipFirstAumRefresh = React.useRef(true)
  React.useEffect(() => {
    if (skipFirstAumRefresh.current) {
      skipFirstAumRefresh.current = false
      return
    }
    setRefreshKey((key) => key + 1)
  }, [headerAumMode])
  // Historical "as of" view: a past date shows holdings, prices and FX as they stood then.
  // Today / a future date is the same as live, so it is treated as no date.
  const [asOfDate, setAsOfDate] = React.useState("")
  const todayYmd = format(new Date(), "yyyy-MM-dd")
  const effectiveAsOf = asOfDate && asOfDate < todayYmd ? asOfDate : ""
  const reportQueryString = React.useMemo(() => {
    if (!effectiveAsOf) return ""
    const qs = new URLSearchParams()
    qs.set("as_of_date", effectiveAsOf)
    return qs.toString()
  }, [effectiveAsOf])

  const handleAsOfBuilt = React.useCallback(() => {
    setRefreshKey((key) => key + 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: PIB_AS_OF_CLIENT,
  })
  const asOfView = deriveAsOfViewState(effectiveAsOf, asOfState)
  const { snapshotReady: asOfSnapshotReady, waiting: asOfWaiting, failed: asOfFailed } = asOfView

  const { report, isLoading: reportLoading, errorMessage } = usePibPivotReport(
    tenant,
    reportQueryString,
    refreshKey,
    asOfSnapshotReady
  )
  const isLoading = reportLoading || asOfWaiting

  // The pivot grid merged into the page-level "PIB Report" grant this page is already gated
  // on (see report-page-guard.tsx) — no separate checkbox/key of its own. Cell drill-down
  // kept its own Portal Access checkbox ("PIB Pivot Detail API"), so it's still gated here.
  const canViewDetail = React.useMemo(
    () => sessionAllowsRouteKeys(allowedRoutes, [CUSTOMER_ROUTE_KEYS.pibPivotDetail]),
    [allowedRoutes],
  )
  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 [activeTab, setActiveTab] = React.useState<PibWorkbookTabId>("pivot")
  const [detailTarget, setDetailTarget] = React.useState<PibPivotDetailTarget | null>(null)
  const [filterStats, setFilterStats] = React.useState({
    visible: 0,
    total: 0,
    active: false,
  })

  useReportBankModeInvalidation(() => {
    setRefreshKey((key) => key + 1)
  })

  const onFilterStatsChange = React.useCallback(
    (stats: { visible: number; total: number; active: boolean }) => {
      setFilterStats(stats)
    },
    []
  )
  const activeMeta = PIB_WORKBOOK_TABS.find((t) => t.id === activeTab)

  const title = report?.title ?? "PIB Advisory — Pivot"
  const periodLabel = asOfWaiting
    ? `Building snapshot as of ${formatYmd(effectiveAsOf)}…`
    : (report?.periodLabel ?? "Loading…")
  const currencyNote = report?.currencyNote ?? ""
  const dataSource = report?.dataSource ?? "live"
  const totalAssetsAdvisedUsd = report?.totalAssetsAdvisedUsd ?? 0
  const assignedAssetsUsd = report?.assignedAssetsUsd ?? 0
  const inferredAssetsUsd = report?.inferredAssetsUsd ?? 0
  const potentialAssetsUsd = report?.potentialAssetsUsd ?? 0
  const loanLiabilityUsd = report?.loanLiabilityUsd ?? 0
  const reportingCurrency = report?.defaultCurrencyCode ?? "USD"
  const aumMode = report?.aumMode ?? "gross"
  // While a first-time snapshot builds, never show rows left over from another date.
  const pivotRows = asOfWaiting ? [] : (report?.pivotRows ?? [])
  const noSnapshot = dataSource === "missing" && !asOfWaiting
  const rowCountLabel = noSnapshot
    ? "No snapshot"
    : filterStats.active
      ? `${formatPlainAmount(filterStats.visible)} of ${formatPlainAmount(filterStats.total)} pivot rows`
      : `${formatPlainAmount(pivotRows.length)} pivot row${pivotRows.length === 1 ? "" : "s"}`
  // An individual login has one client; if the rows say otherwise keep the pivot grid.
  const isIndividual = isIndividualLogin && isSingleClientPivot(pivotRows)
  const sourceBadge =
    isLoading ? "Loading" : dataSource === "snapshot" ? "Snapshot" : dataSource === "missing" ? "No snapshot" : "Live"

  const emptyState = (
    <div className="flex min-h-0 flex-1 flex-col items-center justify-center gap-2 px-6 py-16 text-center">
      <FileSpreadsheet className="size-10 text-muted-foreground/40" />
      <p className="font-medium text-sm">
        {asOfFailed ? "Snapshot could not be built" : noSnapshot ? "No snapshot for this date" : "No pivot data"}
      </p>
      <p className="max-w-sm text-muted-foreground text-xs">
        {asOfFailed ? (
          <>Use Rebuild snapshot to try again, or clear the date to view live holdings.</>
        ) : noSnapshot ? (
          <>
            The snapshot for{" "}
            <span className="font-medium text-foreground">{formatYmd(effectiveAsOf)}</span>{" "}
            is not built yet. Clear the date to view live holdings.
          </>
        ) : effectiveAsOf ? (
          <>No holdings on {formatYmd(effectiveAsOf)}.</>
        ) : (
          <>
            Assign PIB classes in ISIN Asset Master and ensure overall summary rows are
            stamped with <code className="font-mono">pib_class</code>.
          </>
        )}
      </p>
    </div>
  )

  return (
    <div className="flex h-[calc(100dvh-8rem)] min-w-0 w-full flex-col gap-3 overflow-hidden">
      <header className="flex shrink-0 flex-col gap-3 sm:flex-row sm:items-start sm:justify-between">
        <div className="space-y-1">
          <div className="flex flex-wrap items-center gap-2">
            <ClipboardList className="size-5 text-emerald-700 dark:text-emerald-400" />
            <h1 className="font-semibold text-xl tracking-tight">{title}</h1>
            <Badge variant="secondary">{sourceBadge}</Badge>
          </div>
          <p className="text-muted-foreground text-sm">
            {periodLabel}
            {currencyNote ? ` · ${currencyNote}` : ""}
          </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 flex-wrap items-center gap-2">
          <div className="flex items-center gap-1.5">
            <DatePicker
              value={asOfDate}
              placeholder="As of date (live)"
              align="end"
              className="h-9 w-[10.5rem]"
              onChange={(value) => setAsOfDate(value)}
            />
            {asOfDate ? (
              <Button
                type="button"
                variant="ghost"
                size="icon"
                className="size-9 shrink-0"
                aria-label="Clear as-of date (use live data)"
                onClick={() => setAsOfDate("")}
              >
                <X className="size-4" />
              </Button>
            ) : null}
          </div>
          {effectiveAsOf ? (
            <Button
              type="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
            type="button"
            variant="outline"
            size="icon"
            className="size-9 shrink-0"
            aria-label="Refresh report"
            onClick={() => setRefreshKey((key) => key + 1)}
          >
            <RefreshCw className={cn("size-4", isLoading && "animate-spin")} />
          </Button>
        </div>
      </header>

      {effectiveAsOf ? (
        <AsOfSnapshotBanner
          className="shrink-0"
          asOf={effectiveAsOf}
          view={asOfView}
          buildingAsOf={asOfState?.buildingAsOf}
          onBackToLive={() => setAsOfDate("")}
        />
      ) : null}

      {isIndividual ? (
        <ReportLoadingPanel
          loading={isLoading}
          label="Loading PIB report…"
          minHeightClassName="min-h-0"
          className={cn("flex min-h-0 flex-1 flex-col overflow-hidden", isLoading && "pointer-events-none")}
        >
          {pivotRows.length === 0 && !isLoading ? (
            emptyState
          ) : (
            <PibIndividualOverview
              rows={pivotRows}
              currency={reportingCurrency}
              totalAssets={totalAssetsAdvisedUsd}
              assigned={assignedAssetsUsd}
              inferred={inferredAssetsUsd}
              potential={potentialAssetsUsd}
              loanLiability={loanLiabilityUsd}
              aumMode={aumMode}
              periodLabel={periodLabel}
              onOpenDetail={canViewDetail ? setDetailTarget : undefined}
            />
          )}
        </ReportLoadingPanel>
      ) : (
      <>
      <div className="overflow-hidden rounded-xl border border-border/80 bg-card shadow-sm">
        <div className="flex flex-wrap items-baseline gap-x-2.5 gap-y-1 px-4 py-2.5 sm:px-5">
          <span className="font-semibold text-[11px] text-emerald-700 uppercase tracking-wider dark:text-emerald-400">
            B410_7001T — {aumMode === "net" ? "Net assets advised" : "Total assets advised"}
          </span>
          <Badge
            variant="outline"
            className="shrink-0 font-normal text-[10px]"
            title="Follows the AUM setting, like the Overall Summary total"
          >
            {aumMode === "net" ? "Net AUM" : "Gross AUM"}
          </Badge>
          <span className="inline-flex items-center gap-1.5 font-semibold text-lg tabular-nums tracking-tight text-emerald-700 dark:text-emerald-400">
            <span className="font-bold text-sm text-foreground">{reportingCurrency}</span>
            {formatPibValue(totalAssetsAdvisedUsd)}
            <PibSummaryCalcHelp
              currency={reportingCurrency}
              totalAssets={totalAssetsAdvisedUsd}
              assigned={assignedAssetsUsd}
              inferred={inferredAssetsUsd}
              potential={potentialAssetsUsd}
              loanLiability={loanLiabilityUsd}
              aumMode={aumMode}
            />
          </span>
        </div>
      </div>

      <div className="flex min-h-0 flex-1 flex-col overflow-hidden rounded-xl border bg-background shadow-sm">
        <div className="flex shrink-0 items-center justify-between gap-2 border-b bg-muted/30 px-3 py-1.5">
          <div className="flex items-center gap-2 text-xs">
            <FileSpreadsheet className="size-3.5 text-emerald-700 dark:text-emerald-400" />
            <span className="font-medium">{activeMeta?.label ?? activeTab}</span>
          </div>
          <span className="flex flex-wrap items-center justify-end gap-x-2 gap-y-0.5 text-[10px] text-muted-foreground">
            <span>
              {rowCountLabel}
              {activeTab === "pivot" ? " · click an amount to view holdings" : ""}
            </span>
            {activeTab === "pivot" ? (
              <>
                <span className="inline-flex items-center gap-1" title="Holding has no ISIN PIB assignment — bucket inferred from AC">
                  <span className="inline-block size-1.5 rounded-full bg-amber-500" aria-hidden />
                  inferred PIB
                </span>
                <span
                  className="inline-flex items-center"
                  title="Structured Products Potential notes — shown on drill, not included in PIB totals"
                >
                  <ReportLifecycleBadge kind="potential" className="pointer-events-none" />
                </span>
                <span
                  className="inline-flex items-center gap-1"
                  title="Negative market value (accounting style)"
                >
                  <span className="font-medium tabular-nums text-red-600 dark:text-red-400" aria-hidden>
                    (0.00)
                  </span>
                  negative value
                </span>
              </>
            ) : null}
          </span>
        </div>

        <div className="flex min-h-0 flex-1 flex-col overflow-hidden">
          {activeTab === "pivot" ? (
            <ReportLoadingPanel
              loading={isLoading}
              label="Loading PIB pivot…"
              minHeightClassName="min-h-0"
              className={cn("flex min-h-0 flex-1 flex-col overflow-hidden", isLoading && "pointer-events-none")}
            >
              {pivotRows.length === 0 && !isLoading ? (
                emptyState
              ) : (
                <PivotSheet
                  rows={pivotRows}
                  onCellClick={canViewDetail ? setDetailTarget : undefined}
                  onFilterStatsChange={onFilterStatsChange}
                />
              )}
            </ReportLoadingPanel>
          ) : (
            <div className="flex min-h-0 flex-1 flex-col overflow-hidden">
              <PlaceholderSheet label={activeMeta?.label ?? activeTab} />
            </div>
          )}
        </div>

        <WorkbookTabs active={activeTab} onChange={setActiveTab} />
      </div>
      </>
      )}

      <PibPivotDetailDialog
        target={detailTarget}
        asOfDate={effectiveAsOf}
        open={detailTarget != null}
        onOpenChange={(open) => {
          if (!open) setDetailTarget(null)
        }}
      />
    </div>
  )
}
