import { Checkbox } from '@/components/ui/checkbox';
import { useFormatDateInterval } from '@/hooks/use-format-date-interval';
import { useNumber } from '@/hooks/use-numer-formatter';
import { useSelector } from '@/redux';
import type { IChartData } from '@/trpc/client';
import { cn } from '@/utils/cn';
import { getChartColor } from '@/utils/theme';
import type { ColumnDef, Header, Row } from '@tanstack/react-table';
import {
  type ExpandedState,
  type SortingState,
  flexRender,
  getCoreRowModel,
  getExpandedRowModel,
  getFilteredRowModel,
  useReactTable,
} from '@tanstack/react-table';
import {
  type VirtualItem,
  useVirtualizer,
  useWindowVirtualizer,
} from '@tanstack/react-virtual';
import throttle from 'lodash.throttle';
import { ChevronDown, ChevronRight } from 'lucide-react';
import type * as React from 'react';
import { useEffect, useMemo, useRef, useState } from 'react';

import { ReportTableToolbar } from './report-table-toolbar';
import {
  type ExpandableTableRow,
  type GroupedTableRow,
  type TableRow,
  groupsToExpandableRows,
  transformToHierarchicalGroups,
  transformToTableData,
} from './report-table-utils';
import { SerieName } from './serie-name';

declare module '@tanstack/react-table' {
  interface ColumnMeta<TData, TValue> {
    pinned?: 'left' | 'right';
    isBreakdown?: boolean;
    breakdownIndex?: number;
  }
}

interface ReportTableProps {
  data: IChartData;
  visibleSeries: IChartData['series'] | string[];
  setVisibleSeries: React.Dispatch<React.SetStateAction<string[]>>;
}

const DEFAULT_COLUMN_WIDTH = 150;
const ROW_HEIGHT = 48; // h-12

interface VirtualRowProps {
  row: Row<TableRow | GroupedTableRow>;
  virtualRow: VirtualItem;
  pinningStylesMap: Map<string, React.CSSProperties>;
  headers: Header<TableRow | GroupedTableRow, unknown>[];
  isResizingRef: React.MutableRefObject<boolean>;
  resizingColumnId: string | null;
  setResizingColumnId: (id: string | null) => void;
  // Horizontal virtualization props
  leftPinnedColumns: Header<TableRow | GroupedTableRow, unknown>['column'][];
  scrollableColumns: Header<TableRow | GroupedTableRow, unknown>['column'][];
  rightPinnedColumns: Header<TableRow | GroupedTableRow, unknown>['column'][];
  virtualColumns: VirtualItem[];
  leftPinnedWidth: number;
  scrollableColumnsTotalWidth: number;
  rightPinnedWidth: number;
}

const VirtualRow = function VirtualRow({
  row,
  virtualRow,
  pinningStylesMap,
  headers,
  isResizingRef,
  resizingColumnId,
  setResizingColumnId,
  leftPinnedColumns,
  scrollableColumns,
  rightPinnedColumns,
  virtualColumns,
  leftPinnedWidth,
  scrollableColumnsTotalWidth,
  rightPinnedWidth,
}: VirtualRowProps) {
  const cells = row.getVisibleCells();

  const renderCell = (
    column: Header<TableRow | GroupedTableRow, unknown>['column'],
    header: Header<TableRow | GroupedTableRow, unknown> | undefined,
  ) => {
    const cell = cells.find((c) => c.column.id === column.id);
    if (!cell || !header) return null;

    const isBreakdown = column.columnDef.meta?.isBreakdown ?? false;
    const pinningStyles = pinningStylesMap.get(column.id) ?? {};
    const canResize = column.getCanResize();
    const isPinned = column.columnDef.meta?.pinned === 'left';
    const isResizing = resizingColumnId === column.id;

    return (
      <div
        key={cell.id}
        style={{
          width: `${header.getSize()}px`,
          minWidth: column.columnDef.minSize,
          maxWidth: column.columnDef.maxSize,
          ...pinningStyles,
        }}
        className={cn('relative overflow-hidden border-r')}
      >
        {flexRender(cell.column.columnDef.cell, cell.getContext())}
        {canResize && isPinned && (
          <div
            data-resize-handle
            onMouseDown={(e) => {
              e.stopPropagation();
              isResizingRef.current = true;
              setResizingColumnId(column.id);
              header.getResizeHandler()(e);
            }}
            onMouseUp={() => {
              setTimeout(() => {
                isResizingRef.current = false;
                setResizingColumnId(null);
              }, 0);
            }}
            onTouchStart={(e) => {
              e.stopPropagation();
              isResizingRef.current = true;
              setResizingColumnId(column.id);
              header.getResizeHandler()(e);
            }}
            onTouchEnd={() => {
              setTimeout(() => {
                isResizingRef.current = false;
                setResizingColumnId(null);
              }, 0);
            }}
            className={cn(
              'absolute right-0 top-0 h-full w-1 cursor-col-resize touch-none select-none bg-transparent hover:bg-primary/50 transition-colors',
              isResizing && 'bg-primary',
            )}
          />
        )}
      </div>
    );
  };

  return (
    <div
      key={virtualRow.key}
      style={{
        position: 'absolute',
        top: 0,
        left: 0,
        width: leftPinnedWidth + scrollableColumnsTotalWidth + rightPinnedWidth,
        height: `${virtualRow.size}px`,
        transform: `translateY(${virtualRow.start}px)`,
        display: 'flex',
        minWidth: 'fit-content',
      }}
      className="border-b hover:bg-muted/30 transition-colors"
    >
      {/* Left Pinned Columns */}
      {leftPinnedColumns.map((column) => {
        const header = headers.find((h) => h.column.id === column.id);
        return renderCell(column, header);
      })}

      {/* Scrollable Columns (Virtualized) */}
      <div
        style={{
          position: 'relative',
          width: scrollableColumnsTotalWidth,
          height: `${virtualRow.size}px`,
        }}
      >
        {virtualColumns.map((virtualCol) => {
          const column = scrollableColumns[virtualCol.index];
          if (!column) return null;
          const header = headers.find((h) => h.column.id === column.id);
          const cell = cells.find((c) => c.column.id === column.id);
          if (!cell || !header) return null;

          return (
            <div
              key={cell.id}
              style={{
                position: 'absolute',
                left: `${virtualCol.start}px`,
                width: `${virtualCol.size}px`,
                height: `${virtualRow.size}px`,
              }}
              className={cn('relative overflow-hidden')}
            >
              {flexRender(cell.column.columnDef.cell, cell.getContext())}
            </div>
          );
        })}
      </div>

      {/* Right Pinned Columns */}
      {rightPinnedColumns.map((column) => {
        const header = headers.find((h) => h.column.id === column.id);
        return renderCell(column, header);
      })}
    </div>
  );
};

export function ReportTable({
  data,
  visibleSeries,
  setVisibleSeries,
}: ReportTableProps) {
  const [grouped, setGrouped] = useState(false);
  const [expanded, setExpanded] = useState<ExpandedState>({});
  const [sorting, setSorting] = useState<SortingState>([]);
  const [globalFilter, setGlobalFilter] = useState('');
  const [columnSizing, setColumnSizing] = useState<Record<string, number>>({});
  const [resizingColumnId, setResizingColumnId] = useState<string | null>(null);
  const isResizingRef = useRef(false);
  const parentRef = useRef<HTMLDivElement>(null);
  const [scrollMargin, setScrollMargin] = useState(0);
  const number = useNumber();
  const interval = useSelector((state) => state.report.interval);
  const breakdowns = useSelector((state) => state.report.breakdowns);

  const formatDate = useFormatDateInterval({
    interval,
    short: true,
  });

  // Transform data to hierarchical groups or flat rows
  const {
    groups: hierarchicalGroups,
    rows: flatRows,
    dates,
    breakdownPropertyNames,
  } = useMemo(() => {
    if (grouped) {
      const result = transformToHierarchicalGroups(data, breakdowns);
      return {
        groups: result.groups,
        rows: null,
        dates: result.dates,
        breakdownPropertyNames: result.breakdownPropertyNames,
      };
    }
    const result = transformToTableData(data, breakdowns, false);
    return {
      groups: null,
      rows: result.rows as TableRow[],
      dates: result.dates,
      breakdownPropertyNames: result.breakdownPropertyNames,
    };
  }, [data, breakdowns, grouped]);

  // Convert hierarchical groups to expandable rows (for TanStack Table's expanding feature)
  const expandableRows = useMemo(() => {
    if (!grouped || !hierarchicalGroups || hierarchicalGroups.length === 0) {
      return null;
    }

    return groupsToExpandableRows(
      hierarchicalGroups,
      breakdownPropertyNames.length,
    );
  }, [grouped, hierarchicalGroups, breakdownPropertyNames.length]);

  // Use expandable rows if available, otherwise use flat rows
  const rows = expandableRows ?? flatRows ?? [];

  // Filter rows based on global search and apply sorting
  const filteredRows = useMemo(() => {
    let result = rows;

    // Apply search filter
    if (globalFilter.trim()) {
      const searchLower = globalFilter.toLowerCase();
      result = rows.filter((row) => {
        // Search in serie name
        if (row.serieName.toLowerCase().includes(searchLower)) return true;

        // Search in breakdown values
        if (
          row.breakdownValues.some((val) =>
            val?.toLowerCase().includes(searchLower),
          )
        ) {
          return true;
        }

        // Search in metric values
        const metrics = ['count', 'sum', 'average', 'min', 'max'] as const;
        if (
          metrics.some((metric) =>
            String(row[metric]).toLowerCase().includes(searchLower),
          )
        ) {
          return true;
        }

        // Search in date values
        if (
          Object.values(row.dateValues).some((val) =>
            String(val).toLowerCase().includes(searchLower),
          )
        ) {
          return true;
        }

        return false;
      });
    }

    // Apply sorting - if grouped, always sort groups by highest count, then sort within each group
    if (grouped && result.length > 0) {
      const groupedRows = result as ExpandableTableRow[] | GroupedTableRow[];

      // Sort function based on current sort state
      const sortFn = (
        a: ExpandableTableRow | GroupedTableRow | TableRow,
        b: ExpandableTableRow | GroupedTableRow | TableRow,
      ) => {
        // If no sorting is selected, return 0 (no change)
        if (sorting.length === 0) return 0;

        for (const sort of sorting) {
          const { id, desc } = sort;
          let aValue: any;
          let bValue: any;

          if (id === 'serie-name') {
            aValue = a.serieName ?? '';
            bValue = b.serieName ?? '';
          } else if (id.startsWith('breakdown-')) {
            const index = Number.parseInt(id.replace('breakdown-', ''), 10);
            if ('breakdownDisplay' in a && a.breakdownDisplay) {
              aValue = a.breakdownDisplay[index] ?? '';
            } else {
              aValue = a.breakdownValues[index] ?? '';
            }
            if ('breakdownDisplay' in b && b.breakdownDisplay) {
              bValue = b.breakdownDisplay[index] ?? '';
            } else {
              bValue = b.breakdownValues[index] ?? '';
            }
          } else if (id.startsWith('metric-')) {
            const metric = id.replace('metric-', '') as keyof TableRow;
            aValue = a[metric] ?? 0;
            bValue = b[metric] ?? 0;
          } else if (id.startsWith('date-')) {
            const date = id.replace('date-', '');
            aValue = a.dateValues[date] ?? 0;
            bValue = b.dateValues[date] ?? 0;
          } else {
            continue;
          }

          // Handle null/undefined values
          if (aValue == null && bValue == null) continue;
          if (aValue == null) return 1;
          if (bValue == null) return -1;

          // Compare values
          if (typeof aValue === 'string' && typeof bValue === 'string') {
            const comparison = aValue.localeCompare(bValue);
            if (comparison !== 0) return desc ? -comparison : comparison;
          } else {
            if (aValue < bValue) return desc ? 1 : -1;
            if (aValue > bValue) return desc ? -1 : 1;
          }
        }
        return 0;
      };

      // For expandable rows, we need to sort recursively
      function sortExpandableRows(
        rows: ExpandableTableRow[],
        isTopLevel = true,
      ): ExpandableTableRow[] {
        // Sort rows: groups by count first (only at top level), then apply user sort
        const sorted = [...rows].sort((a, b) => {
          // At top level, sort groups by count first
          if (isTopLevel) {
            const aIsGroupHeader = 'isGroupHeader' in a && a.isGroupHeader;
            const bIsGroupHeader = 'isGroupHeader' in b && b.isGroupHeader;

            if (aIsGroupHeader && bIsGroupHeader) {
              const aLevel = 'groupLevel' in a ? (a.groupLevel ?? -1) : -1;
              const bLevel = 'groupLevel' in b ? (b.groupLevel ?? -1) : -1;

              // Same level groups: sort by count first (always, regardless of user sort)
              if (aLevel === bLevel) {
                const aCount = a.count ?? 0;
                const bCount = b.count ?? 0;
                if (aCount !== bCount) {
                  return bCount - aCount; // Highest first
                }
                // If counts are equal, fall through to user sort
              }
            }
          }

          // Apply user's sort criteria (for all rows, including within groups)
          return sortFn(a, b);
        });

        // Sort subRows recursively (within each group) - these are NOT top level
        return sorted.map((row) => {
          if ('subRows' in row && row.subRows) {
            return {
              ...row,
              subRows: sortExpandableRows(row.subRows, false),
            };
          }
          return row;
        });
      }

      return sortExpandableRows(groupedRows as ExpandableTableRow[]);
    }

    // For flat mode, apply sorting
    if (!grouped && result.length > 0 && sorting.length > 0) {
      return [...result].sort((a, b) => {
        for (const sort of sorting) {
          const { id, desc } = sort;
          let aValue: any;
          let bValue: any;

          if (id === 'serie-name') {
            aValue = a.serieName ?? '';
            bValue = b.serieName ?? '';
          } else if (id.startsWith('breakdown-')) {
            const index = Number.parseInt(id.replace('breakdown-', ''), 10);
            aValue = a.breakdownValues[index] ?? '';
            bValue = b.breakdownValues[index] ?? '';
          } else if (id.startsWith('metric-')) {
            const metric = id.replace('metric-', '') as keyof TableRow;
            aValue = a[metric] ?? 0;
            bValue = b[metric] ?? 0;
          } else if (id.startsWith('date-')) {
            const date = id.replace('date-', '');
            aValue = a.dateValues[date] ?? 0;
            bValue = b.dateValues[date] ?? 0;
          } else {
            continue;
          }

          // Handle null/undefined values
          if (aValue == null && bValue == null) continue;
          if (aValue == null) return 1;
          if (bValue == null) return -1;

          // Compare values
          if (typeof aValue === 'string' && typeof bValue === 'string') {
            const comparison = aValue.localeCompare(bValue);
            if (comparison !== 0) return desc ? -comparison : comparison;
          } else {
            if (aValue < bValue) return desc ? 1 : -1;
            if (aValue > bValue) return desc ? -1 : 1;
          }
        }
        return 0;
      });
    }

    return result;
  }, [rows, globalFilter, grouped, sorting]);

  // Calculate min/max values for color visualization
  const { metricRanges, dateRanges } = useMemo(() => {
    const metricRanges: Record<string, { min: number; max: number }> = {
      count: {
        min: Number.POSITIVE_INFINITY,
        max: Number.NEGATIVE_INFINITY,
      },
      sum: { min: Number.POSITIVE_INFINITY, max: Number.NEGATIVE_INFINITY },
      average: {
        min: Number.POSITIVE_INFINITY,
        max: Number.NEGATIVE_INFINITY,
      },
      min: { min: Number.POSITIVE_INFINITY, max: Number.NEGATIVE_INFINITY },
      max: { min: Number.POSITIVE_INFINITY, max: Number.NEGATIVE_INFINITY },
    };

    const dateRanges: Record<string, { min: number; max: number }> = {};
    dates.forEach((date) => {
      dateRanges[date] = {
        min: Number.POSITIVE_INFINITY,
        max: Number.NEGATIVE_INFINITY,
      };
    });

    // Helper function to flatten expandable rows and get only individual rows
    function getIndividualRows(
      rows: (ExpandableTableRow | TableRow)[],
    ): TableRow[] {
      const individualRows: TableRow[] = [];
      for (const row of rows) {
        const isGroupHeader =
          'isGroupHeader' in row && row.isGroupHeader === true;
        const isSummary = 'isSummaryRow' in row && row.isSummaryRow === true;

        if (!isGroupHeader && !isSummary) {
          // It's an individual row - add it
          individualRows.push(row as TableRow);
        }

        // Always recursively process subRows if they exist (regardless of whether this is a group header)
        if ('subRows' in row && row.subRows && Array.isArray(row.subRows)) {
          individualRows.push(...getIndividualRows(row.subRows));
        }
      }
      return individualRows;
    }

    // Get only individual rows from all rows to ensure consistent ranges
    const individualRows = getIndividualRows(rows);
    const isSingleSeries = individualRows.length === 1;

    if (isSingleSeries) {
      // For single series, calculate ranges from date values
      const singleRow = individualRows[0]!;
      const allDateValues = dates.map(
        (date) => singleRow.dateValues[date] ?? 0,
      );
      const dateMin = Math.min(...allDateValues);
      const dateMax = Math.max(...allDateValues);

      // For date columns, use the range across all dates
      dates.forEach((date) => {
        dateRanges[date] = {
          min: dateMin,
          max: dateMax,
        };
      });

      // For metric columns, use date values to create meaningful ranges
      // This ensures we can still show color variation even with one series
      metricRanges.count = { min: dateMin, max: dateMax };
      metricRanges.sum = { min: dateMin, max: dateMax };
      metricRanges.average = { min: dateMin, max: dateMax };
      metricRanges.min = { min: dateMin, max: dateMax };
      metricRanges.max = { min: dateMin, max: dateMax };
    } else {
      // Multiple series: calculate ranges across individual rows only
      if (individualRows.length === 0) {
        // No individual rows found - this shouldn't happen, but handle gracefully
      } else {
        individualRows.forEach((row) => {
          // Calculate metric ranges
          Object.keys(metricRanges).forEach((key) => {
            const value = row[key as keyof typeof row] as number;
            if (typeof value === 'number' && !Number.isNaN(value)) {
              metricRanges[key]!.min = Math.min(metricRanges[key]!.min, value);
              metricRanges[key]!.max = Math.max(metricRanges[key]!.max, value);
            }
          });

          // Calculate date ranges
          dates.forEach((date) => {
            const value = row.dateValues[date] ?? 0;
            if (!dateRanges[date]) {
              dateRanges[date] = {
                min: Number.POSITIVE_INFINITY,
                max: Number.NEGATIVE_INFINITY,
              };
            }
            if (typeof value === 'number' && !Number.isNaN(value)) {
              dateRanges[date]!.min = Math.min(dateRanges[date]!.min, value);
              dateRanges[date]!.max = Math.max(dateRanges[date]!.max, value);
            }
          });
        });
      }
    }

    return { metricRanges, dateRanges };
  }, [rows, dates]);

  // Helper to get background color style and opacity for a value
  // Returns both style and opacity (for text color calculation) to avoid parsing
  const getCellBackgroundStyle = (
    value: number,
    min: number,
    max: number,
    colorClass: 'purple' | 'emerald' = 'emerald',
  ): { style: React.CSSProperties; opacity: number } => {
    if (value === 0) {
      return { style: {}, opacity: 0 };
    }

    // If min equals max (e.g. single row or all values same), show moderate opacity
    let opacity: number;
    if (max === min) {
      opacity = 0.5;
    } else {
      const percentage = (value - min) / (max - min);
      opacity = Math.max(0.05, Math.min(1, percentage));
    }

    // Use rgba colors directly instead of opacity + background class
    const backgroundColor =
      colorClass === 'purple'
        ? `rgba(168, 85, 247, ${opacity})` // purple-500
        : `rgba(16, 185, 129, ${opacity})`; // emerald-500

    return {
      style: { backgroundColor },
      opacity,
    };
  };

  // Normalize visibleSeries to string array
  const visibleSeriesIds = useMemo(() => {
    if (visibleSeries.length === 0) return [];
    if (typeof visibleSeries[0] === 'string') {
      return visibleSeries as string[];
    }
    return (visibleSeries as IChartData['series']).map((s) => s.id);
  }, [visibleSeries]);

  // Create a hash of visibleSeriesIds to track checkbox state changes
  const visibleSeriesIdsHash = useMemo(() => {
    return visibleSeriesIds.sort().join(',');
  }, [visibleSeriesIds]);

  // Get serie index for color
  const getSerieIndex = (serieId: string): number => {
    return data.series.findIndex((s) => s.id === serieId);
  };

  // Toggle serie visibility
  const toggleSerieVisibility = (serieId: string) => {
    setVisibleSeries((prev) => {
      if (prev.includes(serieId)) {
        return prev.filter((id) => id !== serieId);
      }
      return [...prev, serieId];
    });
  };

  // Toggle group collapse (now handled by TanStack Table's expanding feature)
  // This is kept for backward compatibility with header click handlers
  const toggleGroupCollapse = (groupKey: string) => {
    // This will be handled by TanStack Table's row expansion
    // We can find the row by groupKey and toggle it
    // For now, this is a no-op as TanStack Table handles it
  };

  // Define columns
  const columns = useMemo<ColumnDef<TableRow | GroupedTableRow>[]>(() => {
    const cols: ColumnDef<TableRow | GroupedTableRow>[] = [];

    // Serie name column (pinned left) with checkbox
    cols.push({
      id: 'serie-name',
      header: 'Serie',
      accessorKey: 'serieName',
      enableSorting: true,
      size: DEFAULT_COLUMN_WIDTH,
      meta: {
        pinned: 'left',
      },
      cell: ({ row }) => {
        const original = row.original;
        const serieId = original.serieId;
        // Look up serie name directly from data to ensure we always have the latest value
        const serie = data.series.find((s) => s.id === serieId);
        const serieName = serie?.names[0] ?? original.serieName ?? '';
        const isVisible = visibleSeriesIds.includes(serieId);
        const serieIndex = getSerieIndex(serieId);
        const color = getChartColor(serieIndex);

        // Check if this serie name matches the first row in the group (for muted styling)
        let isMuted = false;
        let isFirstRowInGroup = false;
        if (
          grouped &&
          'groupKey' in original &&
          original.groupKey &&
          !original.isSummaryRow
        ) {
          // Find all rows in this group from the current rows array
          const groupRows = rows.filter(
            (r): r is GroupedTableRow =>
              'groupKey' in r &&
              r.groupKey === original.groupKey &&
              !r.isSummaryRow,
          );

          if (groupRows.length > 0) {
            const firstRowInGroup = groupRows[0]!;

            // Check if this is the first row in the group
            if (firstRowInGroup.id === original.id) {
              isFirstRowInGroup = true;
            } else {
              isMuted = true;
            }
          }
        }

        const originalRow = row.original as ExpandableTableRow | TableRow;
        const isGroupHeader =
          'isGroupHeader' in originalRow && originalRow.isGroupHeader === true;
        const isExpanded = grouped ? (row.getIsExpanded?.() ?? false) : false;
        const isSerieGroupHeader =
          isGroupHeader &&
          'groupLevel' in originalRow &&
          originalRow.groupLevel === -1;
        const hasSubRows =
          'subRows' in originalRow && (originalRow.subRows?.length ?? 0) > 0;
        const isExpandable = grouped && isSerieGroupHeader && hasSubRows;

        return (
          <div className="flex items-center gap-2 px-4 h-12">
            <Checkbox
              checked={isVisible}
              onCheckedChange={() => toggleSerieVisibility(serieId)}
              style={{
                borderColor: color,
                backgroundColor: isVisible ? color : 'transparent',
              }}
              className="h-4 w-4 shrink-0"
            />
            <SerieName
              name={serieName}
              className={cn(
                'truncate',
                !isExpandable && grouped && 'text-muted-foreground/40',
                isExpandable && 'font-semibold',
              )}
            />
            {isExpandable && (
              <button
                type="button"
                onClick={(e) => {
                  e.stopPropagation();
                  e.preventDefault();
                  // Toggle expanded state manually
                  setExpanded((prev) => {
                    const newExpanded: ExpandedState =
                      typeof prev === 'object' ? { ...prev } : {};
                    const rowId = row.id;
                    newExpanded[rowId] = !newExpanded[rowId];
                    return newExpanded;
                  });
                }}
                className="cursor-pointer hover:opacity-70"
              >
                {isExpanded ? (
                  <ChevronDown className="h-4 w-4" />
                ) : (
                  <ChevronRight className="h-4 w-4" />
                )}
              </button>
            )}
          </div>
        );
      },
    });

    // Breakdown columns (pinned left, collapsible)
    breakdownPropertyNames.forEach((propertyName, index) => {
      const isLastBreakdown = index === breakdownPropertyNames.length - 1;
      const isCollapsible = grouped && !isLastBreakdown;

      cols.push({
        id: `breakdown-${index}`,
        enableSorting: true,
        enableResizing: true,
        size: columnSizing[`breakdown-${index}`] ?? DEFAULT_COLUMN_WIDTH,
        minSize: 100,
        maxSize: 500,
        accessorFn: (row) => {
          if ('breakdownDisplay' in row && grouped) {
            return row.breakdownDisplay[index] ?? '';
          }
          return row.breakdownValues[index] ?? '';
        },
        header: ({ column }) => {
          if (!isCollapsible) {
            return propertyName;
          }

          // Find all rows at this breakdown level that can be expanded
          const rowsAtLevel: string[] = [];
          if (grouped && expandableRows) {
            function collectRowIdsAtLevel(
              rows: ExpandableTableRow[],
              targetLevel: number,
              currentLevel = 0,
            ): void {
              for (const row of rows) {
                if (
                  row.isGroupHeader &&
                  row.groupLevel === targetLevel &&
                  (row.subRows?.length ?? 0) > 0
                ) {
                  rowsAtLevel.push(row.id);
                }
                // Recurse into subRows if we haven't reached target level yet
                if (currentLevel < targetLevel && row.subRows) {
                  collectRowIdsAtLevel(
                    row.subRows,
                    targetLevel,
                    currentLevel + 1,
                  );
                }
              }
            }
            collectRowIdsAtLevel(expandableRows, index);
          }

          // Check if all groups at this level are expanded
          const allExpanded =
            rowsAtLevel.length > 0 &&
            rowsAtLevel.every(
              (id) => typeof expanded === 'object' && expanded[id] === true,
            );

          return (
            <div
              className="flex items-center gap-2 cursor-pointer hover:opacity-70"
              onClick={() => {
                if (!grouped) return;
                // Toggle all groups at this breakdown level
                setExpanded((prev) => {
                  const newExpanded: ExpandedState =
                    typeof prev === 'object' ? { ...prev } : {};
                  const shouldExpand = !allExpanded;
                  rowsAtLevel.forEach((id) => {
                    newExpanded[id] = shouldExpand;
                  });
                  return newExpanded;
                });
              }}
              onKeyDown={(e) => {
                if (e.key === 'Enter' || e.key === ' ') {
                  e.preventDefault();
                  if (!grouped) return;
                  setExpanded((prev) => {
                    const newExpanded: ExpandedState =
                      typeof prev === 'object' ? { ...prev } : {};
                    const shouldExpand = !allExpanded;
                    rowsAtLevel.forEach((id) => {
                      newExpanded[id] = shouldExpand;
                    });
                    return newExpanded;
                  });
                }
              }}
              role="button"
              tabIndex={0}
            >
              <span>{propertyName}</span>
            </div>
          );
        },
        meta: {
          pinned: 'left',
          isBreakdown: true,
        },
        cell: ({ row }) => {
          const original = row.original as ExpandableTableRow | TableRow;
          const isGroupHeader =
            'isGroupHeader' in original && original.isGroupHeader === true;
          const canExpand = row.getCanExpand?.() ?? false;
          const isExpanded = row.getIsExpanded?.() ?? false;

          const value: string | number | null =
            original.breakdownValues[index] ?? null;
          const isLastBreakdown = index === breakdownPropertyNames.length - 1;
          const isMuted = (!isLastBreakdown && !canExpand && grouped) || !value;

          // For group headers, only show value at the group level, hide deeper breakdowns
          if (isGroupHeader && 'groupLevel' in original) {
            const groupLevel = original.groupLevel ?? 0;
            if (index !== groupLevel) {
              return <div className="flex items-center gap-2 px-4 h-12" />;
            }
          }

          return (
            <div className="flex items-center gap-2 px-4 h-12">
              <span
                className={cn(
                  'truncate block leading-[48px]',
                  isMuted && 'text-muted-foreground/50',
                  isGroupHeader && 'font-semibold',
                )}
              >
                {value || '(Not set)'}
              </span>
              {canExpand &&
                index ===
                  ('groupLevel' in original ? (original.groupLevel ?? 0) : 0) &&
                index < breakdownPropertyNames.length - 1 && (
                  <button
                    type="button"
                    onClick={(e) => {
                      e.stopPropagation();
                      const handler = row.getToggleExpandedHandler();
                      if (handler) handler();
                    }}
                    className="cursor-pointer hover:opacity-70"
                  >
                    {isExpanded ? (
                      <ChevronDown className="h-4 w-4" />
                    ) : (
                      <ChevronRight className="h-4 w-4" />
                    )}
                  </button>
                )}
            </div>
          );
        },
      });
    });

    // Metric columns
    const metrics = [
      { key: 'count', label: 'Unique' },
      { key: 'sum', label: 'Sum' },
      { key: 'average', label: 'Average' },
      { key: 'min', label: 'Min' },
      { key: 'max', label: 'Max' },
    ] as const;

    metrics.forEach((metric) => {
      cols.push({
        id: `metric-${metric.key}`,
        header: metric.label,
        accessorKey: metric.key,
        enableSorting: true,
        size: 100,
        cell: ({ row }) => {
          const value = row.original[metric.key];
          const original = row.original as ExpandableTableRow | TableRow;
          const hasIsSummaryRow = 'isSummaryRow' in original;
          const hasIsGroupHeader = 'isGroupHeader' in original;
          const isSummary = hasIsSummaryRow && original.isSummaryRow === true;
          const isGroupHeader =
            hasIsGroupHeader && original.isGroupHeader === true;
          const isIndividualRow = !isSummary && !isGroupHeader;
          const range = metricRanges[metric.key];

          // Only apply colors to individual rows, not summary or group header rows
          // Also check that range is valid (not still at initial values)
          const hasValidRange =
            range &&
            range.min !== Number.POSITIVE_INFINITY &&
            range.max !== Number.NEGATIVE_INFINITY;

          const { style: backgroundStyle, opacity: bgOpacity } =
            isIndividualRow && hasValidRange
              ? getCellBackgroundStyle(value, range.min, range.max, 'purple')
              : { style: {}, opacity: 0 };

          return (
            <div
              className={cn(
                'h-12 w-full text-right font-mono text-sm px-4 flex items-center justify-end',
                '[text-shadow:_0_0_3px_rgb(0_0_0_/_20%)] shadow-[inset_-1px_-1px_0_var(--border)]',
                (isSummary || isGroupHeader) && 'font-semibold',
              )}
              style={backgroundStyle}
            >
              {number.format(value)}
            </div>
          );
        },
      });
    });

    // Date columns
    dates.forEach((date) => {
      cols.push({
        id: `date-${date}`,
        header: formatDate(date),
        accessorFn: (row) => row.dateValues[date] ?? 0,
        enableSorting: true,
        size: 100,
        cell: ({ row }) => {
          const value = row.original.dateValues[date] ?? 0;
          const isSummary = row.original.isSummaryRow ?? false;
          const isGroupHeader =
            'isGroupHeader' in row.original &&
            row.original.isGroupHeader === true;
          const isIndividualRow = !isSummary && !isGroupHeader;
          const range = dateRanges[date];
          // Only apply colors to individual rows, not summary or group header rows
          // Also check that range is valid (not still at initial values)
          const hasValidRange =
            range &&
            range.min !== Number.POSITIVE_INFINITY &&
            range.max !== Number.NEGATIVE_INFINITY;
          const { style: backgroundStyle, opacity: bgOpacity } =
            isIndividualRow && hasValidRange
              ? getCellBackgroundStyle(value, range.min, range.max, 'emerald')
              : { style: {}, opacity: 0 };

          const needsLightText = bgOpacity > 0.7;

          return (
            <div
              className={cn(
                'h-12 w-full text-right font-mono text-sm px-4 flex items-center justify-end',
                '[text-shadow:_0_0_3px_rgb(0_0_0_/_20%)] shadow-[inset_-1px_-1px_0_var(--border)]',
                (isSummary || isGroupHeader) && 'font-semibold',
              )}
              style={backgroundStyle}
            >
              {number.format(value)}
            </div>
          );
        },
      });
    });

    return cols;
  }, [
    breakdownPropertyNames,
    dates,
    formatDate,
    number,
    grouped,
    visibleSeriesIds,
    expandableRows,
    rows,
    metricRanges,
    dateRanges,
    columnSizing,
    expanded,
    data,
  ]);

  // Create a hash of column IDs to track when columns change
  const columnsHash = useMemo(() => {
    return columns.map((col) => col.id).join(',');
  }, [columns]);

  // Memoize table options to ensure table updates when filteredRows changes
  const tableOptions = useMemo(
    () => ({
      data: filteredRows, // This is already sorted in filteredRows
      columns,
      getCoreRowModel: getCoreRowModel(),
      getExpandedRowModel: grouped ? getExpandedRowModel() : undefined,
      getSubRows: grouped
        ? (row: ExpandableTableRow | TableRow) =>
            'subRows' in row ? row.subRows : undefined
        : undefined,
      // Sorting is handled manually in filteredRows, so we don't use getSortedRowModel
      getFilteredRowModel: getFilteredRowModel(),
      filterFns: {
        isWithinRange: () => true,
      },
      enableColumnResizing: true,
      columnResizeMode: 'onChange' as const,
      getRowCanExpand: grouped
        ? (row: any) => {
            const r = row.original as ExpandableTableRow;
            if (!('isGroupHeader' in r) || !r.isGroupHeader) return false;
            // Don't allow expansion for the last breakdown level
            const groupLevel = r.groupLevel ?? -1;
            const isLastBreakdown =
              groupLevel === breakdownPropertyNames.length - 1;
            const hasSubRows = (r.subRows?.length ?? 0) > 0;
            return !isLastBreakdown && hasSubRows;
          }
        : undefined,
      state: {
        sorting, // Keep sorting state for UI indicators
        columnSizing,
        expanded: grouped ? expanded : undefined,
      },
      onSortingChange: setSorting,
      onColumnSizingChange: setColumnSizing,
      onExpandedChange: grouped ? setExpanded : undefined,
      globalFilterFn: () => true, // We handle filtering manually
      manualSorting: true, // We handle sorting manually for both modes
      manualFiltering: true, // We handle filtering manually
    }),
    [
      filteredRows,
      columns,
      grouped,
      breakdownPropertyNames.length,
      sorting,
      columnSizing,
      expanded,
      setSorting,
      setColumnSizing,
      setExpanded,
    ],
  );

  const table = useReactTable(tableOptions);

  // Virtualization setup
  useEffect(() => {
    const updateScrollMargin = throttle(() => {
      if (parentRef.current) {
        setScrollMargin(
          parentRef.current.getBoundingClientRect().top + window.scrollY,
        );
      }
    }, 500);

    updateScrollMargin();
    window.addEventListener('resize', updateScrollMargin);

    return () => {
      window.removeEventListener('resize', updateScrollMargin);
    };
  }, []);

  // Handle global mouseup to reset resize flag
  useEffect(() => {
    const handleMouseUp = () => {
      if (isResizingRef.current) {
        // Small delay to ensure resize handlers complete
        setTimeout(() => {
          isResizingRef.current = false;
          setResizingColumnId(null);
        }, 100);
      }
    };

    window.addEventListener('mouseup', handleMouseUp);
    window.addEventListener('touchend', handleMouseUp);

    return () => {
      window.removeEventListener('mouseup', handleMouseUp);
      window.removeEventListener('touchend', handleMouseUp);
    };
  }, []);

  // Get the row model to use (expanded when grouped, regular otherwise)
  // Always call the table helpers so React Table can manage its own memoization
  const rowModelToUse = grouped
    ? table.getExpandedRowModel()
    : table.getRowModel();

  const virtualizer = useWindowVirtualizer({
    count: rowModelToUse.rows.length,
    estimateSize: () => ROW_HEIGHT,
    overscan: 10,
    scrollMargin,
  });

  const virtualRows = virtualizer.getVirtualItems();

  // Get visible columns in order
  const headerColumns = table
    .getAllLeafColumns()
    .filter((col) => table.getState().columnVisibility[col.id] !== false);

  // Separate columns into pinned and scrollable
  const leftPinnedColumns = headerColumns.filter(
    (col) => col.columnDef.meta?.pinned === 'left',
  );
  const rightPinnedColumns = headerColumns.filter(
    (col) => col.columnDef.meta?.pinned === 'right',
  );
  const scrollableColumns = headerColumns.filter(
    (col) => !col.columnDef.meta?.pinned,
  );

  // Calculate widths for virtualization
  const leftPinnedWidth = useMemo(
    () => leftPinnedColumns.reduce((sum, col) => sum + col.getSize(), 0),
    [leftPinnedColumns, columnSizing],
  );
  const rightPinnedWidth = useMemo(
    () => rightPinnedColumns.reduce((sum, col) => sum + col.getSize(), 0),
    [rightPinnedColumns, columnSizing],
  );
  const scrollableColumnsTotalWidth = useMemo(
    () => scrollableColumns.reduce((sum, col) => sum + col.getSize(), 0),
    [scrollableColumns, columnSizing],
  );

  // Horizontal virtualization for scrollable columns
  // Only virtualize if we have enough columns to benefit from it
  const shouldVirtualizeHorizontal = scrollableColumns.length > 10;

  const horizontalVirtualizer = useVirtualizer({
    count: scrollableColumns.length,
    getScrollElement: () => parentRef.current,
    estimateSize: (index) =>
      scrollableColumns[index]?.getSize() ?? DEFAULT_COLUMN_WIDTH,
    horizontal: true,
    overscan: shouldVirtualizeHorizontal ? 5 : scrollableColumns.length,
  });

  // Get virtual columns - if not virtualizing, return all columns
  const virtualColumns = shouldVirtualizeHorizontal
    ? horizontalVirtualizer.getVirtualItems()
    : scrollableColumns.map((col, index) => ({
        index,
        start: scrollableColumns
          .slice(0, index)
          .reduce((sum, c) => sum + c.getSize(), 0),
        size: col.getSize(),
        key: col.id,
        end: 0,
        lane: 0,
      }));

  // Pre-compute grid template columns string and headers
  const { gridTemplateColumns, headers } = useMemo(() => {
    const headerGroups = table.getHeaderGroups();
    const firstGroupHeaders = headerGroups[0]?.headers ?? [];
    return {
      gridTemplateColumns:
        firstGroupHeaders.map((h) => `${h.getSize()}px`).join(' ') ?? '',
      headers: firstGroupHeaders,
    };
  }, [table, columnSizing, columnsHash]);

  // Pre-compute pinning styles for all columns
  const pinningStylesMap = useMemo(() => {
    const stylesMap = new Map<string, React.CSSProperties>();
    const headerGroups = table.getHeaderGroups();

    headerGroups.forEach((group) => {
      group.headers.forEach((header) => {
        const column = header.column;
        const isPinned = column.columnDef.meta?.pinned;
        if (!isPinned) {
          stylesMap.set(column.id, {});
          return;
        }

        const pinnedColumns =
          isPinned === 'left' ? leftPinnedColumns : rightPinnedColumns;
        const columnIndex = pinnedColumns.findIndex((c) => c.id === column.id);
        const isLastPinned =
          columnIndex === pinnedColumns.length - 1 && isPinned === 'left';
        const isFirstRightPinned = columnIndex === 0 && isPinned === 'right';

        let left = 0;
        if (isPinned === 'left') {
          for (let i = 0; i < columnIndex; i++) {
            left += pinnedColumns[i]!.getSize();
          }
        }

        stylesMap.set(column.id, {
          position: 'sticky' as const,
          left: isPinned === 'left' ? `${left}px` : undefined,
          right: isPinned === 'right' ? '0px' : undefined,
          zIndex: 10,
          backgroundColor: 'var(--card)',
          boxShadow: isLastPinned
            ? '-4px 0 4px -4px var(--border) inset'
            : isFirstRightPinned
              ? '4px 0 4px -4px var(--border) inset'
              : undefined,
        });
      });
    });

    return stylesMap;
  }, [table, leftPinnedColumns, rightPinnedColumns, columnSizing, columnsHash]);

  // Helper to get pinning styles (for backward compatibility with header)
  const getPinningStyles = (
    column: ReturnType<typeof table.getColumn> | undefined,
  ) => {
    if (!column) return {};
    return pinningStylesMap.get(column.id) ?? {};
  };

  if (rows.length === 0) {
    return null;
  }

  return (
    <div className="flex flex-col border rounded-lg overflow-hidden bg-card mt-8">
      <ReportTableToolbar
        grouped={grouped}
        onToggleGrouped={
          !breakdowns || breakdowns.length === 0
            ? undefined
            : () => setGrouped(!grouped)
        }
        search={globalFilter}
        onSearchChange={setGlobalFilter}
        onUnselectAll={() => setVisibleSeries([])}
      />
      <div
        ref={parentRef}
        className="overflow-x-auto"
        style={{
          width: '100%',
        }}
      >
        <div
          className="relative"
          style={{
            width:
              leftPinnedWidth + scrollableColumnsTotalWidth + rightPinnedWidth,
            minWidth: 'fit-content',
          }}
        >
          {/* Header */}
          <div
            className="sticky top-0 z-20 bg-card border-b"
            style={{
              display: 'flex',
              width:
                leftPinnedWidth +
                scrollableColumnsTotalWidth +
                rightPinnedWidth,
              minWidth: 'fit-content',
            }}
          >
            {/* Left Pinned Columns */}
            {leftPinnedColumns.map((column) => {
              const header = headers.find((h) => h.column.id === column.id);
              if (!header) return null;
              const headerContent = column.columnDef.header;
              const isBreakdown = column.columnDef.meta?.isBreakdown ?? false;
              const pinningStyles = getPinningStyles(column);
              const isMetricOrDate =
                column.id.startsWith('metric-') ||
                column.id.startsWith('date-');

              const canSort = column.getCanSort();
              const isSorted = column.getIsSorted();
              const canResize = column.getCanResize();
              const isPinned = column.columnDef.meta?.pinned === 'left';

              return (
                <div
                  key={header.id}
                  style={{
                    width: `${header.getSize()}px`,
                    minWidth: column.columnDef.minSize,
                    maxWidth: column.columnDef.maxSize,
                    ...pinningStyles,
                  }}
                  className={cn(
                    'h-10 px-4 flex items-center text-[10px] uppercase font-semibold bg-muted/30 border-r border-border whitespace-nowrap relative',
                    isMetricOrDate && 'text-right',
                    canSort && 'cursor-pointer hover:bg-muted/50 select-none',
                  )}
                  onClick={
                    canSort
                      ? (e) => {
                          // Don't trigger sort if clicking on resize handle or if we just finished resizing
                          if (
                            isResizingRef.current ||
                            column.getIsResizing() ||
                            (e.target as HTMLElement).closest(
                              '[data-resize-handle]',
                            )
                          ) {
                            return;
                          }
                          column.toggleSorting();
                        }
                      : undefined
                  }
                  onKeyDown={
                    canSort
                      ? (e) => {
                          if (e.key === 'Enter' || e.key === ' ') {
                            e.preventDefault();
                            column.toggleSorting();
                          }
                        }
                      : undefined
                  }
                  role={canSort ? 'button' : undefined}
                  tabIndex={canSort ? 0 : undefined}
                >
                  <div className="flex items-center gap-1.5 flex-1">
                    {header.isPlaceholder
                      ? null
                      : typeof headerContent === 'function'
                        ? flexRender(headerContent, header.getContext())
                        : headerContent}
                    {canSort && (
                      <span className="text-muted-foreground">
                        {isSorted === 'asc'
                          ? '↑'
                          : isSorted === 'desc'
                            ? '↓'
                            : '⇅'}
                      </span>
                    )}
                  </div>
                  {canResize && isPinned && (
                    <div
                      data-resize-handle
                      onMouseDown={(e) => {
                        e.stopPropagation();
                        isResizingRef.current = true;
                        setResizingColumnId(column.id);
                        header.getResizeHandler()(e);
                      }}
                      onMouseUp={() => {
                        // Use setTimeout to allow the resize to complete before resetting
                        setTimeout(() => {
                          isResizingRef.current = false;
                          setResizingColumnId(null);
                        }, 0);
                      }}
                      onTouchStart={(e) => {
                        e.stopPropagation();
                        isResizingRef.current = true;
                        setResizingColumnId(column.id);
                        header.getResizeHandler()(e);
                      }}
                      onTouchEnd={() => {
                        setTimeout(() => {
                          isResizingRef.current = false;
                          setResizingColumnId(null);
                        }, 0);
                      }}
                      className={cn(
                        'absolute right-0 top-0 h-full w-1 cursor-col-resize touch-none select-none bg-transparent hover:bg-primary/50 transition-colors',
                        header.column.getIsResizing() && 'bg-primary',
                      )}
                    />
                  )}
                </div>
              );
            })}

            {/* Scrollable Columns (Virtualized) */}
            <div
              style={{
                position: 'relative',
                width: scrollableColumnsTotalWidth,
                height: '40px',
              }}
            >
              {virtualColumns.map((virtualCol) => {
                const column = scrollableColumns[virtualCol.index];
                if (!column) return null;
                const header = headers.find((h) => h.column.id === column.id);
                if (!header) return null;

                const headerContent = header.column.columnDef.header;
                const isBreakdown =
                  header.column.columnDef.meta?.isBreakdown ?? false;
                const isMetricOrDate =
                  header.column.id.startsWith('metric-') ||
                  header.column.id.startsWith('date-');
                const canSort = header.column.getCanSort();
                const isSorted = header.column.getIsSorted();

                return (
                  <div
                    key={header.id}
                    style={{
                      position: 'absolute',
                      left: `${virtualCol.start}px`,
                      width: `${virtualCol.size}px`,
                      height: '40px',
                    }}
                    className={cn(
                      'px-4 flex items-center text-[10px] uppercase font-semibold bg-muted/30 border-r border-border whitespace-nowrap',
                      isMetricOrDate && 'text-right',
                      canSort && 'cursor-pointer hover:bg-muted/50 select-none',
                    )}
                    onClick={
                      canSort
                        ? (e) => {
                            if (
                              isResizingRef.current ||
                              header.column.getIsResizing() ||
                              (e.target as HTMLElement).closest(
                                '[data-resize-handle]',
                              )
                            ) {
                              return;
                            }
                            header.column.toggleSorting();
                          }
                        : undefined
                    }
                    onKeyDown={
                      canSort
                        ? (e) => {
                            if (e.key === 'Enter' || e.key === ' ') {
                              e.preventDefault();
                              header.column.toggleSorting();
                            }
                          }
                        : undefined
                    }
                    role={canSort ? 'button' : undefined}
                    tabIndex={canSort ? 0 : undefined}
                  >
                    <div className="flex items-center gap-1.5 flex-1">
                      {header.isPlaceholder
                        ? null
                        : typeof headerContent === 'function'
                          ? flexRender(headerContent, header.getContext())
                          : headerContent}
                      {canSort && (
                        <span className="text-muted-foreground">
                          {isSorted === 'asc'
                            ? '↑'
                            : isSorted === 'desc'
                              ? '↓'
                              : '⇅'}
                        </span>
                      )}
                    </div>
                  </div>
                );
              })}
            </div>

            {/* Right Pinned Columns */}
            {rightPinnedColumns.map((column) => {
              const header = headers.find((h) => h.column.id === column.id);
              if (!header) return null;

              const headerContent = header.column.columnDef.header;
              const isBreakdown =
                header.column.columnDef.meta?.isBreakdown ?? false;
              const pinningStyles = getPinningStyles(header.column);
              const isMetricOrDate =
                header.column.id.startsWith('metric-') ||
                header.column.id.startsWith('date-');
              const canSort = header.column.getCanSort();
              const isSorted = header.column.getIsSorted();
              const canResize = header.column.getCanResize();

              return (
                <div
                  key={header.id}
                  style={{
                    width: `${header.getSize()}px`,
                    minWidth: header.column.columnDef.minSize,
                    maxWidth: header.column.columnDef.maxSize,
                    ...pinningStyles,
                  }}
                  className={cn(
                    'h-10 px-4 flex items-center text-[10px] uppercase font-semibold bg-muted/30 border-r border-border whitespace-nowrap relative',
                    isMetricOrDate && 'text-right',
                    canSort && 'cursor-pointer hover:bg-muted/50 select-none',
                  )}
                  onClick={
                    canSort
                      ? (e) => {
                          if (
                            isResizingRef.current ||
                            header.column.getIsResizing() ||
                            (e.target as HTMLElement).closest(
                              '[data-resize-handle]',
                            )
                          ) {
                            return;
                          }
                          header.column.toggleSorting();
                        }
                      : undefined
                  }
                  onKeyDown={
                    canSort
                      ? (e) => {
                          if (e.key === 'Enter' || e.key === ' ') {
                            e.preventDefault();
                            header.column.toggleSorting();
                          }
                        }
                      : undefined
                  }
                  role={canSort ? 'button' : undefined}
                  tabIndex={canSort ? 0 : undefined}
                >
                  <div className="flex items-center gap-1.5 flex-1">
                    {header.isPlaceholder
                      ? null
                      : typeof headerContent === 'function'
                        ? flexRender(headerContent, header.getContext())
                        : headerContent}
                    {canSort && (
                      <span className="text-muted-foreground">
                        {isSorted === 'asc'
                          ? '↑'
                          : isSorted === 'desc'
                            ? '↓'
                            : '⇅'}
                      </span>
                    )}
                  </div>
                </div>
              );
            })}
          </div>

          {/* Virtualized Body */}
          <div
            style={{
              height: `${virtualizer.getTotalSize()}px`,
              position: 'relative',
            }}
          >
            {virtualRows.map((virtualRow) => {
              const tableRow = rowModelToUse.rows[virtualRow.index];
              if (!tableRow) return null;

              // Include serie name in key to force re-render when name changes
              const serieId = tableRow.original.serieId;
              const serie = data.series.find((s) => s.id === serieId);
              const serieName =
                serie?.names[0] ?? tableRow.original.serieName ?? '';

              return (
                <VirtualRow
                  key={`${virtualRow.key}-${serieName}-${gridTemplateColumns}`}
                  row={tableRow}
                  virtualRow={{
                    ...virtualRow,
                    start: virtualRow.start - virtualizer.options.scrollMargin,
                  }}
                  pinningStylesMap={pinningStylesMap}
                  headers={headers}
                  isResizingRef={isResizingRef}
                  resizingColumnId={resizingColumnId}
                  setResizingColumnId={setResizingColumnId}
                  leftPinnedColumns={leftPinnedColumns}
                  scrollableColumns={scrollableColumns}
                  rightPinnedColumns={rightPinnedColumns}
                  virtualColumns={virtualColumns}
                  leftPinnedWidth={leftPinnedWidth}
                  scrollableColumnsTotalWidth={scrollableColumnsTotalWidth}
                  rightPinnedWidth={rightPinnedWidth}
                />
              );
            })}
          </div>
        </div>
      </div>
    </div>
  );
}
