import { useState, useEffect, useRef, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { Icon } from '../../Atoms/Icon/Icon';
import { Skeleton } from '../../Atoms/Skeleton/Skeleton';
import { DBGrid } from '../../DataDisplay/DBGrid/DBGrid';
import { FONT_FAMILY_SANS, FONT_FAMILY_MONO } from '../../../designTokens';
import { FiltrosAvanzados } from '../FiltrosAvanzados/FiltrosAvanzados';
import { AppliedFilters } from '../AppliedFilters/AppliedFilters';
import { TABLA_GRID_INLINE_STYLES } from './tablaGridInlineStyles.js';
import { TablaSearch } from './TablaSearch.jsx';
import {
  tiempoRelativoEnEspanol,
  computeRegistrosRangoDesdeHasta,
  computeRegistrosRangoDesdeHastaConPageSize,
  clampRegistrosPorPagina,
} from './tablaRegistrosAndTimeUtils.js';
import { RegistrosHastaInput } from './RegistrosHastaInput.jsx';
import {
  RED_BAR_RECORDS_TEXT_STYLE,
  RED_BAR_RECORDS_RANGE_NUMBER_STYLE,
} from './tablaRedBarStyles.js';

export {
  tiempoRelativoEnEspanol,
  computeRegistrosRangoDesdeHasta,
  computeRegistrosRangoDesdeHastaConPageSize,
  clampRegistrosPorPagina,
  clampRegistrosHastaEditable,
  resolvePageAfterPageSizeChange,
} from './tablaRegistrosAndTimeUtils.js';
export { RED_BAR_RECORDS_TEXT_STYLE, RED_BAR_RECORDS_RANGE_NUMBER_STYLE };
export { RegistrosHastaInput } from './RegistrosHastaInput.jsx';

const MOBILE_BREAKPOINT_PX = 768;
const INTERMEDIATE_BREAKPOINT_MIN_PX = 1069;
const INTERMEDIATE_BREAKPOINT_MAX_PX = 1149;

function useIsMobile() {
  const [isMobile, setIsMobile] = useState(() =>
    typeof window !== 'undefined' ? window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT_PX}px)`).matches : false
  );
  useEffect(() => {
    if (typeof window === 'undefined') return;
    const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT_PX}px)`);
    const handle = () => setIsMobile(mql.matches);
    mql.addEventListener('change', handle);
    return () => mql.removeEventListener('change', handle);
  }, []);
  return isMobile;
}

/** SM + MD (0–1023px); desktop no usa este hook para la barra roja estable. */
const NARROW_LAYOUT_MAX_PX = 1023;
function useIsNarrowLayout() {
  const [narrow, setNarrow] = useState(() =>
    typeof window !== 'undefined' ? window.matchMedia(`(max-width: ${NARROW_LAYOUT_MAX_PX}px)`).matches : false
  );
  useEffect(() => {
    if (typeof window === 'undefined') return;
    const mql = window.matchMedia(`(max-width: ${NARROW_LAYOUT_MAX_PX}px)`);
    const handle = () => setNarrow(mql.matches);
    mql.addEventListener('change', handle);
    return () => mql.removeEventListener('change', handle);
  }, []);
  return narrow;
}

const TABLE_SKELETON_ROWS = 5;
/** Selector del elemento que hace scroll horizontal en MUI X DataGrid (medición barra verde). */
const DATA_GRID_VIRTUAL_SCROLLER_SELECTOR = '.MuiDataGrid-virtualScroller';
const TABLE_PROGRESS_TRACK_COLOR = '#C5D1D6';
const TABLE_PROGRESS_FILL_COLOR = '#3F6F75';
const TABLE_PROGRESS_MIN_THUMB_PCT = 10;
const TABLE_VERTICAL_PROGRESS_TRACK_COLOR = '#D3DDE0';
const TABLE_VERTICAL_PROGRESS_FILL_COLOR = '#3F6F75';
const TABLE_VERTICAL_PROGRESS_MIN_THUMB_PCT = 10;
const TABLE_COLUMNS = [
  { key: 'nombreComercial' },
  { key: 'pais' },
  { key: 'estado' },
  { key: 'actividad' },
  { key: 'acciones' },
];

/** Acciones en masa: IBM Plex Mono Medium 14/21, letter-spacing 1%, leading-trim none. */
export const RED_BAR_BULK_ACTION_BUTTON_STYLE = {
  fontFamily: FONT_FAMILY_MONO,
  fontWeight: 500,
  fontStyle: 'normal',
  fontSize: '14px',
  lineHeight: '21px',
  letterSpacing: '1%',
  leadingTrim: 'none',
  verticalAlign: 'middle',
  color: '#38656D',
  whiteSpace: 'nowrap',
  border: 'none',
  background: 'none',
  padding: 0,
  cursor: 'pointer',
  display: 'inline-flex',
  alignItems: 'center',
  gap: '6px',
};

/** Entre “Editar en masa”, “Archivar” y “Exportar”. */
export const RED_BAR_BULK_ACTION_SEPARATOR_STYLE = {
  width: 1,
  height: 14,
  flexShrink: 0,
  alignSelf: 'center',
  backgroundColor: '#5F5F5F',
};

const HEADER_BG_CLASS = 'anima-tabla__grid-header';

const DEFAULT_TABLE_COLUMNS = [
  { field: 'administrador', headerName: 'Administrador', flex: 1, minWidth: 80, headerClassName: HEADER_BG_CLASS },
  { field: 'email', headerName: 'Email', flex: 1, minWidth: 80, headerClassName: HEADER_BG_CLASS },
  { field: 'rolTecnico', headerName: 'Rol técnico', flex: 1, minWidth: 80, headerClassName: HEADER_BG_CLASS },
  {
    field: 'ultimoAcceso',
    headerName: 'Último acceso',
    flex: 1,
    minWidth: 80,
    headerClassName: HEADER_BG_CLASS,
    renderCell: (params) => tiempoRelativoEnEspanol(params.value),
  },
];

const DEFAULT_TABLE_ROWS = [
  { id: 1, administrador: 'Usuario 1', email: 'usuario1@ejemplo.com', rolTecnico: 'Admin', ultimoAcceso: '01/02/2025' },
  { id: 2, administrador: 'Usuario 2', email: 'usuario2@ejemplo.com', rolTecnico: 'Editor', ultimoAcceso: '28/01/2025' },
  { id: 3, administrador: 'Usuario 3', email: 'usuario3@ejemplo.com', rolTecnico: 'Lector', ultimoAcceso: '15/01/2025' },
];

/** Iconos por defecto para la barra de herramienta de la tabla (heroicons outline): descargar, tabla, cuadrícula.
 *  El del medio (vista tabla) se deja con onClick; el último (vista cuadrícula) lleva href para redirigir a otra ruta. */
const TABLA_TOOLBAR_ICONS_DEFAULT = [
  { iconName: 'ArrowDownTrayIcon', ariaLabel: 'Descargar', onClick: undefined },
  { iconName: 'TableCellsIcon', ariaLabel: 'Vista tabla', onClick: undefined },
  { iconName: 'Squares2X2Icon', ariaLabel: 'Vista cuadrícula', href: '/vista-cuadricula' },
];

const TABLE_HEADER_HEIGHT = 48;
/** Color del icono y texto del botón Filtros (grey-700). */
const TABLA_FILTERS_ICON_COLOR = '#474747';
const TABLA_FILTERS_ACTIVE_COLOR = '#6D3856';
/** Icono Descargar en la toolbar (main-700); mismo tono en desktop y mobile. */
const TABLA_TOOLBAR_DOWNLOAD_ICON_COLOR = '#38656D';
const TABLE_ROW_HEIGHT = 48;
const MIN_TABLE_ROWS = 1;

function TablaSortChevronIcon({ className, sortingOrder, fontSize, style, ...rest }) {
  return (
    <Icon
      name="ChevronUpDownIcon"
      variant="20-solid"
      size={20}
      className={className}
      style={{ color: 'var(--color-cyan-19)', ...(style || {}) }}
      {...rest}
    />
  );
}

/**
 * Contenedor de búsqueda, barra de estado y `DataGrid` con estilos Anima.
 * Props extra de grid (`onRowClick`, `getRowClassName`, `tableSx`) se reenvían al `DataGrid`.
 * Con selección: `onBulkEditClick`, `onBulkArchiveClick`, `onBulkExportClick` para la barra de acciones en masa.
 * `redBarSteadyLeftOnNarrow`: en ≤1023px la columna “Registros” no cambia al seleccionar (ListaPersonas).
 */
export const Tabla = ({
  children,
  containerStyle,
  containerClassName = '',
  contentContainerStyle,
  contentContainerClassName = '',
  className = '',
  // Búsqueda
  searchValue,
  onSearchChange,
  searchPlaceholder,
  // Botón filtros (opcional, al final de la barra de búsqueda)
  showFiltersButton = false,
  filtersButtonLabel,
  filtersCount,
  activeFiltersCount,
  onFiltersClick,
  advancedFiltersModalTitle,
  advancedFiltersModalContent,
  advancedFiltersModalFooter,
  advancedFiltersModalSize,
  advancedFiltersApplyLabel,
  advancedFiltersCancelLabel,
  onAdvancedFiltersApply,
  onAdvancedFiltersCancel,
  advancedFiltersModalProps,
  /** Fila de filtros personalizada (ej. componente `Filtros`) debajo del buscador. */
  filtersArea,
  /** Filtros aplicados renderizados por el DS debajo del buscador. `filtersArea` tiene prioridad si se informa. */
  appliedFilters,
  onAppliedFilterRemove,
  onAppliedFiltersClear,
  showAppliedFiltersLabel = true,
  appliedFiltersLabel,
  appliedFiltersClearLabel,
  /** Clase CSS del contenedor de `filtersArea`. */
  filtersAreaContainerClassName = '',
  /** Estilos inline del contenedor de `filtersArea`. */
  filtersAreaContainerStyle,
  // Tabla
  tableLoading = false,
  users = [],
  tableColumns,
  // Registros (texto: "Registros: N de total", N = registros por página, editable si hay callback)
  registrosCurrent,
  tableTotalCount,
  registrosRangoDesde,
  registrosRangoHasta,
  pageSize,
  /** Cantidad de filas seleccionadas (texto a la izquierda; “Registros” pasa junto a “Página”). */
  selectedCount = 0,
  /** Acciones en masa junto a “n seleccionadas” (solo con selección). */
  onBulkEditClick,
  onBulkArchiveClick,
  onBulkExportClick,
  // Paginación (ej. "Página 1 de 10")
  currentPage = 1,
  totalPages = 1,
  onPrevPage,
  onNextPage,
  // Estado vacío
  sinContenido = false,
  emptyStateLabel,
  // Barra de iconos (toolbar) sobre la tabla: array de { iconName, onClick?, ariaLabel? }
  toolbarIcons = [],
  /** Número de filas del skeleton de carga. Por defecto 5. ConfiguracionUsuarios puede pasar 4. */
  skeletonRowCount,
  /** Altura en px de cada fila del grid (default 48). Aumentar para más espacio vertical en celdas. */
  rowHeight = TABLE_ROW_HEIGHT,
  /** MUI DataGrid: clic en fila (p. ej. alternar checkbox en ListaPersonas). */
  onRowClick,
  /** MUI DataGrid: clase por fila (p. ej. fila seleccionada con checkbox). */
  getRowClassName,
  /** Estilos extra del DataGrid (se fusionan con los de Tabla). */
  tableSx,
  /** Si se pasa, sustituye al DataGrid (y a la barra de scroll horizontal custom). Ej. vista tarjetas en ListaPersonas. */
  tableArea,
  /** Muestra barra vertical externa sincronizada con el scroll del grid (opt-in). */
  showExternalVerticalScroll = false,
  /** Alto visible del viewport de tabla cuando se usa scroll vertical externo. */
  externalVerticalScrollViewportHeight = 320,
  /** Debug visual de métricas del scroll vertical externo (uso temporal). */
  debugExternalVerticalScroll = false,
  /**
   * Desactiva la virtualización del DataGrid (filas y columnas).
   * Necesario si las primeras columnas usan `position: sticky`: con virtualización, al hacer scroll horizontal
   * esas columnas dejan de montarse en el DOM y el comportamiento se rompe.
   */
  disableVirtualization = false,
  /**
   * Si es true: en viewport ≤1023px (SM/MD) la izquierda de la barra roja sigue mostrando solo “Registros: …”
   * al seleccionar filas (sin bloque “n seleccionadas” ni acciones en masa) para no mover el layout.
   */
  redBarSteadyLeftOnNarrow = false,
  /**
   * Al confirmar el número central (solo dígitos): **registros por página** (`clampRegistrosPorPagina`: si total ≥ 10 entre 10 y min(100, total); si total es menor a 10 entre 1 y total); la app debe actualizar tamaño de página y paginación.
   * Si no se pasa, el número sigue siendo solo lectura.
   */
  onRegistrosPorPaginaCommit,
  ...props
}) => {
  const { t } = useTranslation();
  const rowsToShow = sinContenido ? [] : (users.length > 0 ? users : DEFAULT_TABLE_ROWS);
  const skeletonRows = skeletonRowCount ?? TABLE_SKELETON_ROWS;
  const skeletonColumns =
    tableColumns && tableColumns.length > 0
      ? tableColumns.map((c) => ({ key: c.field }))
      : TABLE_COLUMNS;
  const tableScrollRef = useRef(null);
  const gridRootRef = useRef(null);
  const tableScrollRafRef = useRef(null);
  const tableVerticalScrollRafRef = useRef(null);
  const activeHScrollRef = useRef(null);
  const activeVScrollRef = useRef(null);
  const [scrollState, setScrollState] = useState({ scrollLeft: 0, clientWidth: 0, scrollWidth: 0 });
  const [verticalScrollState, setVerticalScrollState] = useState({ scrollTop: 0, clientHeight: 0, scrollHeight: 0 });
  const [isDraggingTableScroll, setIsDraggingTableScroll] = useState(false);
  const [isDraggingVerticalTableScroll, setIsDraggingVerticalTableScroll] = useState(false);
  const [isAdvancedFiltersModalOpen, setIsAdvancedFiltersModalOpen] = useState(false);
  const [internalActiveFiltersCount, setInternalActiveFiltersCount] = useState(0);
  const totalRegistros = rowsToShow.length;
  /** El DataGrid trae paginación interna (10 por defecto); forzamos una “página” que cubre todas las filas que ya paginó el padre. */
  const tablaGridPaginationModel = useMemo(
    () => ({ page: 0, pageSize: Math.max(rowsToShow.length, 1) }),
    [rowsToShow.length],
  );
  const handleTablaGridPaginationModelChange = useCallback(() => {
    /* Paginación en la barra roja; aquí no se cambia el modelo del grid. */
  }, []);
  const registrosN = sinContenido ? 0 : (registrosCurrent !== undefined && registrosCurrent !== null ? registrosCurrent : totalRegistros);
  const totalRegistrosLabel =
    sinContenido ? 0 : (tableTotalCount !== undefined && tableTotalCount !== null
      ? tableTotalCount
      : totalRegistros);
  const hasPrevPage = !sinContenido && currentPage > 1;
  const hasNextPage = !sinContenido && currentPage < totalPages;
  const shouldMoveRegistrosNextToPage = !tableLoading && !sinContenido && (selectedCount ?? 0) > 0;
  /** Booleano estable: si el padre pasa `tableArea`, no hay DataGrid montado; al volver a la tabla hay que reenganchar medición del scroll. */
  const hasTableAreaOverride = tableArea != null;

  const isMobile = useIsMobile();
  const isNarrowLayout = useIsNarrowLayout();
  /** En escritorio (>1023px) el comportamiento con selección no cambia. */
  const showSelectionClusterInRedBar =
    shouldMoveRegistrosNextToPage && !(redBarSteadyLeftOnNarrow && isNarrowLayout);
  /** Solo con selección en escritorio: la izquierda muestra “n seleccionadas…”, entonces “Registros” pasa junto a Página. En SM/MD estable no se duplica. */
  const showDuplicateRegistrosBesidePage =
    shouldMoveRegistrosNextToPage && showSelectionClusterInRedBar;

  /**
   * SM/MD (≤1023px), solo vista tabla (`tableArea` no definido): fila bajo el paginado con
   * “n seleccionadas” + select de acciones en masa (no en vista tarjetas ni en desktop ancho).
   */
  const showNarrowTableBulkRow =
    isNarrowLayout &&
    tableArea == null &&
    !tableLoading &&
    !sinContenido &&
    (selectedCount ?? 0) > 0;

  const handleNarrowBulkSelectChange = useCallback(
    (e) => {
      const v = e.target.value;
      if (v === 'edit') onBulkEditClick?.();
      else if (v === 'archive') onBulkArchiveClick?.();
      else if (v === 'export') onBulkExportClick?.();
      e.target.selectedIndex = 0;
    },
    [onBulkEditClick, onBulkArchiveClick, onBulkExportClick],
  );

  /** SM + MD (≤1023px): botón filtros solo con icono adjustments-horizontal 24×24. */
  const filtersToolbarLabel = filtersButtonLabel ?? t('table.filters', { defaultValue: 'Filtros' });
  const controlledFiltersCount = activeFiltersCount ?? filtersCount;
  const hasAppliedFilters = Array.isArray(appliedFilters) && appliedFilters.length > 0;
  const effectiveFiltersCount = Number.isFinite(controlledFiltersCount)
    ? controlledFiltersCount
    : hasAppliedFilters
      ? appliedFilters.length
    : internalActiveFiltersCount;
  const hasActiveFilters = effectiveFiltersCount > 0;
  const filtersToolbarDisplayLabel = hasActiveFilters
    ? `${filtersToolbarLabel} (${effectiveFiltersCount})`
    : filtersToolbarLabel;
  const showFiltersButtonIcon = !isNarrowLayout;
  const handleFiltersButtonClick = useCallback(
    (event) => {
      if (typeof onFiltersClick === 'function') {
        onFiltersClick(event);
        return;
      }
      setIsAdvancedFiltersModalOpen(true);
    },
    [onFiltersClick],
  );

  const countAppliedAdvancedFilters = useCallback((payload) => {
    const conditionsCount = Array.isArray(payload?.conditions)
      ? payload.conditions.filter((condition) => {
          const value = condition?.value;
          if (Array.isArray(value)) return value.length > 0;
          if (value === null || value === undefined) return false;
          return String(value).trim().length > 0;
        }).length
      : 0;
    const hasAiFilter = typeof payload?.aiValue === 'string' && payload.aiValue.trim().length > 0;
    return conditionsCount + (hasAiFilter ? 1 : 0);
  }, []);

  const handleAdvancedFiltersApply = useCallback(
    (payload) => {
      if (!Number.isFinite(controlledFiltersCount)) {
        setInternalActiveFiltersCount(countAppliedAdvancedFilters(payload));
      }
      onAdvancedFiltersApply?.(payload);
      advancedFiltersModalProps?.onApply?.(payload);
    },
    [controlledFiltersCount, countAppliedAdvancedFilters, onAdvancedFiltersApply, advancedFiltersModalProps],
  );

  const handleAdvancedFiltersClear = useCallback(() => {
    if (!Number.isFinite(controlledFiltersCount)) {
      setInternalActiveFiltersCount(0);
    }
    onAppliedFiltersClear?.();
    advancedFiltersModalProps?.onClear?.();
  }, [controlledFiltersCount, onAppliedFiltersClear, advancedFiltersModalProps]);

  const [isIntermediateWidth, setIsIntermediateWidth] = useState(() =>
    typeof window !== 'undefined'
      ? window.matchMedia(`(min-width: ${INTERMEDIATE_BREAKPOINT_MIN_PX}px) and (max-width: ${INTERMEDIATE_BREAKPOINT_MAX_PX}px)`).matches
      : false
  );

  useEffect(() => {
    if (typeof window === 'undefined') return;
    const mql = window.matchMedia(`(min-width: ${INTERMEDIATE_BREAKPOINT_MIN_PX}px) and (max-width: ${INTERMEDIATE_BREAKPOINT_MAX_PX}px)`);
    const handle = () => setIsIntermediateWidth(mql.matches);
    mql.addEventListener('change', handle);
    return () => mql.removeEventListener('change', handle);
  }, []);
  const showTableProgressBar = scrollState.scrollWidth > scrollState.clientWidth + 1;

  /** Evita “10 de 3”: el tamaño de página efectivo nunca supera el total ni rompe min/máx por total. */
  const pageSizeParaRango = useMemo(() => {
    if (!Number.isFinite(pageSize) || pageSize <= 0 || totalRegistrosLabel <= 0) return undefined;
    return clampRegistrosPorPagina(pageSize, totalRegistrosLabel);
  }, [pageSize, totalRegistrosLabel]);

  const registrosRango = useMemo(() => {
    const hasExplicitRange =
      Number.isFinite(registrosRangoDesde) && Number.isFinite(registrosRangoHasta);
    if (hasExplicitRange) {
      return { desde: registrosRangoDesde, hasta: registrosRangoHasta };
    }
    if (pageSizeParaRango !== undefined) {
      return computeRegistrosRangoDesdeHastaConPageSize({
        sinContenido,
        totalRegistrosLabel,
        registrosEnPagina: registrosN,
        currentPage,
        pageSize: pageSizeParaRango,
      });
    }
    return computeRegistrosRangoDesdeHasta({
      sinContenido,
      totalRegistrosLabel,
      registrosEnPagina: registrosN,
      currentPage,
      totalPages,
    });
  }, [
    sinContenido,
    totalRegistrosLabel,
    registrosN,
    currentPage,
    totalPages,
    registrosRangoDesde,
    registrosRangoHasta,
    pageSizeParaRango,
  ]);

  const registrosPorPaginaLabel = useMemo(() => {
    if (sinContenido || totalRegistrosLabel === 0) return 0;
    if (registrosRango.desde === 0 && registrosRango.hasta === 0) return 0;
    if (pageSizeParaRango !== undefined) return pageSizeParaRango;
    return Math.max(1, registrosRango.hasta - registrosRango.desde + 1);
  }, [sinContenido, totalRegistrosLabel, pageSizeParaRango, registrosRango.desde, registrosRango.hasta]);

  const middleTableWrapClassName = useMemo(
    () =>
      [
        'anima-tabla__middle-table-wrap',
        showTableProgressBar ? '' : 'anima-tabla__middle-table-wrap--no-progress',
        showNarrowTableBulkRow ? 'anima-tabla__middle-table-wrap--after-narrow-bulk' : '',
      ]
        .filter(Boolean)
        .join(' '),
    [showTableProgressBar, showNarrowTableBulkRow],
  );

  /**
   * El scroll horizontal debe medirse siempre en el virtualScroller del DataGrid.
   * Si además el wrapper externo tiene overflow-x y minWidth, se duplica el “ancho scrollable”
   * y la barra verde / el layout parecen tener más recorrido del necesario.
   */
  const getHorizontalScrollEl = () => {
    const gridRoot = gridRootRef.current;
    if (gridRoot) {
      const virtualScroller = gridRoot.querySelector(DATA_GRID_VIRTUAL_SCROLLER_SELECTOR);
      if (virtualScroller) return virtualScroller;
    }
    return tableScrollRef.current;
  };

  const getVerticalScrollEl = () => {
    const gridRoot = gridRootRef.current;
    if (gridRoot) {
      // Prioridad absoluta: el scroller vertical real del DataGrid.
      const virtualScroller = gridRoot.querySelector(DATA_GRID_VIRTUAL_SCROLLER_SELECTOR);
      if (virtualScroller) return virtualScroller;
      // Fallbacks defensivos por cambios de versión de MUI.
      const mainScroller = gridRoot.querySelector('.MuiDataGrid-main');
      if (mainScroller) return mainScroller;
      const windowScroller = gridRoot.querySelector('.MuiDataGrid-window');
      if (windowScroller) return windowScroller;
    }
    return tableScrollRef.current;
  };

  const syncTableScrollState = (preferredEl) => {
    const el = preferredEl || activeHScrollRef.current || getHorizontalScrollEl();
    if (!el) return;
    activeHScrollRef.current = el;
    if (tableScrollRafRef.current) return;
    tableScrollRafRef.current = window.requestAnimationFrame(() => {
      tableScrollRafRef.current = null;
      setScrollState((prev) => {
        const next = {
          scrollLeft: el.scrollLeft,
          clientWidth: el.clientWidth,
          scrollWidth: el.scrollWidth,
        };
        if (
          prev.scrollLeft === next.scrollLeft &&
          prev.clientWidth === next.clientWidth &&
          prev.scrollWidth === next.scrollWidth
        ) {
          return prev;
        }
        return next;
      });
    });
  };

  const syncVerticalTableScrollState = (preferredEl) => {
    const el = preferredEl || activeVScrollRef.current || getVerticalScrollEl();
    if (!el) return;
    activeVScrollRef.current = el;
    if (tableVerticalScrollRafRef.current) return;
    tableVerticalScrollRafRef.current = window.requestAnimationFrame(() => {
      tableVerticalScrollRafRef.current = null;
      setVerticalScrollState((prev) => {
        const next = {
          scrollTop: el.scrollTop,
          clientHeight: el.clientHeight,
          scrollHeight: el.scrollHeight,
        };
        if (
          prev.scrollTop === next.scrollTop &&
          prev.clientHeight === next.clientHeight &&
          prev.scrollHeight === next.scrollHeight
        ) {
          return prev;
        }
        return next;
      });
    });
  };

  useEffect(() => {
    syncTableScrollState();
    syncVerticalTableScrollState();
    const syncAfterMountTimerId = window.setTimeout(() => {
      const gridRoot = gridRootRef.current;
      const scroller = gridRoot?.querySelector?.(DATA_GRID_VIRTUAL_SCROLLER_SELECTOR);
      if (scroller) activeHScrollRef.current = scroller;
      if (scroller) activeVScrollRef.current = scroller;
      syncTableScrollState(scroller || undefined);
      syncVerticalTableScrollState(scroller || undefined);
    }, 0);
    const onResize = () => {
      syncTableScrollState();
      syncVerticalTableScrollState();
    };
    window.addEventListener('resize', onResize);
 
    const outer = tableScrollRef.current;
    const gridRoot = gridRootRef.current;
    const scroller = gridRoot?.querySelector?.(DATA_GRID_VIRTUAL_SCROLLER_SELECTOR);
    const onScroll = (e) => {
      syncTableScrollState(e?.target);
      syncVerticalTableScrollState(e?.target);
    };
    outer?.addEventListener?.('scroll', onScroll, { passive: true });
    scroller?.addEventListener?.('scroll', onScroll, { passive: true });

    let resizeObserver;
    if (scroller && typeof ResizeObserver !== 'undefined') {
      resizeObserver = new ResizeObserver(() => {
        syncTableScrollState(scroller);
        syncVerticalTableScrollState(scroller);
      });
      resizeObserver.observe(scroller);
    }

    return () => {
      window.removeEventListener('resize', onResize);
      window.clearTimeout(syncAfterMountTimerId);
      outer?.removeEventListener?.('scroll', onScroll);
      scroller?.removeEventListener?.('scroll', onScroll);
      resizeObserver?.disconnect();
      if (tableScrollRafRef.current) {
        window.cancelAnimationFrame(tableScrollRafRef.current);
        tableScrollRafRef.current = null;
      }
      if (tableVerticalScrollRafRef.current) {
        window.cancelAnimationFrame(tableVerticalScrollRafRef.current);
        tableVerticalScrollRafRef.current = null;
      }
    };
  }, [isMobile, sinContenido, tableLoading, rowsToShow.length, tableColumns, hasTableAreaOverride]);

  /** Tras quitar `tableArea` el grid se monta de nuevo; MUI puede medir scrollWidth un poco después. */
  useEffect(() => {
    if (hasTableAreaOverride) return;
    const run = () => {
      const gridRoot = gridRootRef.current;
      const scroller = gridRoot?.querySelector?.(DATA_GRID_VIRTUAL_SCROLLER_SELECTOR);
      if (scroller) activeHScrollRef.current = scroller;
      if (scroller) activeVScrollRef.current = scroller;
      syncTableScrollState(scroller || undefined);
      syncVerticalTableScrollState(scroller || undefined);
    };
    run();
    const t1 = window.setTimeout(run, 50);
    const t2 = window.setTimeout(run, 200);
    return () => {
      window.clearTimeout(t1);
      window.clearTimeout(t2);
    };
  }, [hasTableAreaOverride]);

  const minTableHeight = TABLE_HEADER_HEIGHT + MIN_TABLE_ROWS * rowHeight;
  /** Con scroll vertical externo, forzamos viewport fijo para habilitar scroll interno Y del grid. */
  const useExternalVerticalViewport =
    showExternalVerticalScroll && !sinContenido && !tableLoading && tableArea == null;
  /** Con datos reales: altura automática (todas las filas visibles, sin scroll vertical en el grid). */
  const showAutoHeightGrid = !sinContenido && !tableLoading && !useExternalVerticalViewport;
  /** Skeleton: alto fijo según filas del placeholder. */
  const skeletonAreaHeight = Math.max(
    TABLE_HEADER_HEIGHT + skeletonRows * rowHeight,
    minTableHeight,
  );
  const bottomAreaHeight = showAutoHeightGrid
    ? 'auto'
    : useExternalVerticalViewport
      ? `${externalVerticalScrollViewportHeight}px`
    : sinContenido
      ? `${minTableHeight}px`
      : `${skeletonAreaHeight}px`;
  const showTableExternalVerticalBar =
    showExternalVerticalScroll &&
    tableArea == null &&
    !tableLoading &&
    !sinContenido;

  const renderTableContent = () => (
    <div
      className="anima-tabla__bottom-area"
      style={{
        width: '100%',
        flexShrink: 0,
        height: bottomAreaHeight,
        minHeight: `${minTableHeight}px`,
        borderRadius: 0,
        boxSizing: 'border-box',
        display: 'flex',
        flexDirection: 'column',
        overflow: 'hidden',
        ...(isMobile && { margin: 0, padding: 0 }),
      }}
    >
      <div
        style={{
          width: '100%',
          minWidth: 0,
          display: showTableExternalVerticalBar ? 'flex' : 'block',
          alignItems: 'stretch',
          gap: showTableExternalVerticalBar ? '8px' : 0,
        }}
      >
        <div
          ref={tableScrollRef}
          className="anima-tabla__table-scroll-x"
          onScroll={(e) => {
            syncTableScrollState(e?.target);
            syncVerticalTableScrollState(e?.target);
          }}
          style={{
            flex: showTableExternalVerticalBar ? (showAutoHeightGrid ? '0 0 auto' : 1) : undefined,
            width: '100%',
            minHeight: showAutoHeightGrid ? undefined : 0,
            minWidth: 0,
            borderRadius: '12px',
            /* El DataGrid hace scroll X en .MuiDataGrid-virtualScroller; evitar segundo scroll aquí */
            overflowX: 'hidden',
            overflowY: 'hidden',
          }}
        >
        {sinContenido ? (
          <div
            className="anima-tabla__grid anima-tabla__empty-state"
            style={{
              height: '100%',
              width: '100%',
              minWidth: 0,
              borderRadius: '12px',
              border: '1px solid var(--color-grey-400, #919191)',
              boxSizing: 'border-box',
              display: 'flex',
              alignItems: 'center',
              justifyContent: 'center',
              background: 'var(--color-neutrals-white, #FFFFFF)',
            }}
          >
            <span
              style={{
                fontFamily: FONT_FAMILY_SANS,
                fontWeight: 400,
                fontSize: '14px',
                lineHeight: '21px',
                color: 'var(--color-grey-600, #5F5F5F)',
              }}
            >
              {emptyStateLabel ?? t('table.noData', { defaultValue: 'Sin contenido' })}
            </span>
          </div>
        ) : tableLoading ? (
          <div
            className="anima-tabla__grid anima-tabla__grid-skeleton"
            style={{
              height: '100%',
              width: '100%',
              minWidth: 0,
              padding: '12px 16px 20px',
              boxSizing: 'border-box',
              display: 'flex',
              flexDirection: 'column',
              gap: '12px',
            }}
          >
            {/* Cabecera: cada columna 146x20, gap 10, border-radius 4 */}
            <div
              style={{
                display: 'flex',
                gap: '10px',
                alignItems: 'center',
                paddingBottom: '8px',
                borderBottom: '1px solid var(--color-grey-200, #C4C4C4)',
                background: '#F3F3F3',
                margin: '-12px -16px 0',
                padding: '12px 16px 8px',
                flexShrink: 0,
              }}
            >
              {skeletonColumns.map((col) => (
                <div key={col.key} style={{ flex: 1, minWidth: 0, display: 'flex', justifyContent: 'center' }}>
                  <div
                    style={{
                      width: 146,
                      height: 20,
                      borderRadius: 4,
                      opacity: 1,
                      overflow: 'hidden',
                    }}
                  >
                    <Skeleton variant="rectangular" height={20} width={146} />
                  </div>
                </div>
              ))}
            </div>
            {/* Filas: primera columna ancha 234x40; resto 174x20 */}
            {Array.from({ length: skeletonRows }).map((_, rowIndex) => (
              <div
                key={rowIndex}
                style={{
                  display: 'flex',
                  gap: '10px',
                  alignItems: 'center',
                  borderRadius: 4,
                  opacity: 1,
                  flexShrink: 0,
                }}
              >
                {skeletonColumns.map((col, colIndex) => (
                  <div
                    key={col.key}
                    style={{
                      flex: colIndex === 0 ? '0 0 auto' : 1,
                      minWidth: 0,
                      display: 'flex',
                      justifyContent: 'center',
                    }}
                  >
                    <div
                      style={{
                        width: colIndex === 0 ? 234 : 174,
                        height: colIndex === 0 ? 40 : 20,
                        borderRadius: 4,
                        opacity: 1,
                        overflow: 'hidden',
                      }}
                    >
                      <Skeleton
                        variant="rectangular"
                        height={colIndex === 0 ? 40 : 20}
                        width={colIndex === 0 ? 234 : 174}
                      />
                    </div>
                  </div>
                ))}
              </div>
            ))}
          </div>
        ) : (
          <div
            style={{
              height: showAutoHeightGrid ? 'auto' : '100%',
              width: '100%',
              minWidth: 0,
              boxSizing: 'border-box',
            }}
          >
            <div
              ref={gridRootRef}
              style={{
                height: showAutoHeightGrid ? 'auto' : '100%',
                width: '100%',
                minWidth: 0,
              }}
            >
              <DBGrid
                autoHeight={showAutoHeightGrid}
                height={
                  useExternalVerticalViewport
                    ? externalVerticalScrollViewportHeight
                    : (showAutoHeightGrid ? 'auto' : '100%')
                }
                className="anima-tabla__grid"
                containerSx={{ borderRadius: '12px', overflow: 'hidden' }}
                columns={tableColumns ?? DEFAULT_TABLE_COLUMNS}
                rows={rowsToShow}
                paginationModel={tablaGridPaginationModel}
                onPaginationModelChange={handleTablaGridPaginationModelChange}
                slots={{
                  columnUnsortedIcon: TablaSortChevronIcon,
                  columnSortedAscendingIcon: TablaSortChevronIcon,
                  columnSortedDescendingIcon: TablaSortChevronIcon,
                }}
                hideFooter
                scrollbarSize={0}
                rowHeight={rowHeight}
                columnHeaderHeight={TABLE_HEADER_HEIGHT}
                disableColumnMenu
                disableColumnSelector
                onRowClick={onRowClick}
                getRowClassName={getRowClassName}
                disableVirtualization={disableVirtualization}
                sx={{
                  '& .MuiDataGrid-columnHeaders': {
                    backgroundColor: 'var(--color-main-100, #D3DFE1)',
                  },
                  '& .MuiDataGrid-virtualScroller': {
                    overflowY: 'auto !important',
                  },
                  '& .MuiDataGrid-columnHeader': {
                    backgroundColor: 'var(--color-main-100, #D3DFE1)',
                  },
                  '& .MuiDataGrid-columnHeader, & .MuiDataGrid-cell': {
                    borderRight: '2px solid var(--color-grey-200, #C4C4C4)',
                  },
                  '& .MuiDataGrid-columnHeader[data-field="acciones"], & .MuiDataGrid-cell[data-field="acciones"]': {
                    borderRight: 'none',
                  },
                  '& .MuiDataGrid-columnHeader[data-field="ultimoAcceso"], & .MuiDataGrid-cell[data-field="ultimoAcceso"]': {
                    borderRight: 'none',
                  },
                  ...(tableSx || {}),
                }}
              />
            </div>
          </div>
        )}
        </div>
        {showTableExternalVerticalBar && (
          <div
            style={{
              width: '8px',
              minWidth: '8px',
              borderRadius: '9999px',
              backgroundColor: TABLE_VERTICAL_PROGRESS_TRACK_COLOR,
              position: 'relative',
              overflow: 'hidden',
            }}
            aria-hidden="true"
            onMouseDown={(e) => {
              if (e.button !== 0) return;
              const { clientHeight, scrollHeight } = verticalScrollState;
              const canScroll = scrollHeight > clientHeight + 1;
              if (!canScroll) return;
              const el = activeVScrollRef.current || getVerticalScrollEl();
              if (!el) return;
              const track = e.currentTarget;
              const rect = track.getBoundingClientRect();
              const y = Math.max(0, Math.min(e.clientY - rect.top, rect.height));
              const ratio = rect.height > 0 ? y / rect.height : 0;
              const maxScrollTop = Math.max(0, scrollHeight - clientHeight);
              el.scrollTop = ratio * maxScrollTop;
              syncVerticalTableScrollState(el);
            }}
          >
            {(() => {
              const { scrollTop, clientHeight, scrollHeight } = verticalScrollState;
              const canScroll = scrollHeight > clientHeight + 1;
              if (!canScroll) {
                return (
                  <div
                    style={{
                      position: 'absolute',
                      left: 0,
                      top: 0,
                      width: '100%',
                      height: '100%',
                      borderRadius: '9999px',
                      backgroundColor: TABLE_VERTICAL_PROGRESS_FILL_COLOR,
                      opacity: 0.35,
                    }}
                  />
                );
              }
              const maxScrollTop = Math.max(0, scrollHeight - clientHeight);
              const thumbHeightPct = Math.max(
                TABLE_VERTICAL_PROGRESS_MIN_THUMB_PCT,
                (clientHeight / scrollHeight) * 100
              );
              const travelPct = 100 - thumbHeightPct;
              const thumbTopPct = maxScrollTop > 0 ? (scrollTop / maxScrollTop) * travelPct : 0;

              return (
                <div
                  style={{
                    position: 'absolute',
                    left: 0,
                    top: `${thumbTopPct}%`,
                    width: '100%',
                    height: `${thumbHeightPct}%`,
                    borderRadius: '9999px',
                    backgroundColor: TABLE_VERTICAL_PROGRESS_FILL_COLOR,
                    transition: isDraggingVerticalTableScroll ? 'none' : 'top 0.1s ease, height 0.1s ease',
                    cursor: 'grab',
                  }}
                  onMouseDown={(e) => {
                    if (e.button !== 0) return;
                    e.stopPropagation();
                    const el = activeVScrollRef.current || getVerticalScrollEl();
                    if (!el) return;
                    const track = e.currentTarget.parentElement;
                    if (!track) return;
                    const trackRect = track.getBoundingClientRect();
                    const trackHeight = trackRect.height;
                    const thumbHeightPx = (thumbHeightPct / 100) * trackHeight;
                    const travelPx = Math.max(1, trackHeight - thumbHeightPx);
                    const startY = e.clientY;
                    const startTop = el.scrollTop;
                    const initialUserSelect = document.body.style.userSelect;
                    setIsDraggingVerticalTableScroll(true);
                    document.body.style.userSelect = 'none';
                    const onMove = (moveEvt) => {
                      const dy = moveEvt.clientY - startY;
                      const ratio = dy / travelPx;
                      const nextTop = startTop + ratio * (scrollHeight - clientHeight);
                      el.scrollTop = Math.max(0, Math.min(scrollHeight - clientHeight, nextTop));
                      syncVerticalTableScrollState(el);
                    };
                    const onUp = () => {
                      setIsDraggingVerticalTableScroll(false);
                      document.body.style.userSelect = initialUserSelect;
                      document.removeEventListener('mousemove', onMove);
                      document.removeEventListener('mouseup', onUp);
                    };
                    document.addEventListener('mousemove', onMove);
                    document.addEventListener('mouseup', onUp);
                  }}
                />
              );
            })()}
          </div>
        )}
      </div>
    </div>
  );

  const renderTableProgressBar = () => (
    (() => {
      const { scrollLeft, clientWidth, scrollWidth } = scrollState;
      const canScroll = scrollWidth > clientWidth + 1;
      const maxScrollLeft = Math.max(0, scrollWidth - clientWidth);
      const thumbWidthPct = canScroll
        ? Math.max(TABLE_PROGRESS_MIN_THUMB_PCT, (clientWidth / scrollWidth) * 100)
        : 100;
      const travelPct = 100 - thumbWidthPct;
      const thumbLeftPctRaw = canScroll && maxScrollLeft > 0
        ? (scrollLeft / maxScrollLeft) * travelPct
        : 0;
      const thumbLeftPct = Math.round(thumbLeftPctRaw * 1000) / 1000;

      const scrollToTrackPosition = (clientX, trackEl) => {
        if (!trackEl || !canScroll) return;
        const rect = trackEl.getBoundingClientRect();
        const x = Math.max(0, Math.min(clientX - rect.left, rect.width));
        const ratio = rect.width > 0 ? x / rect.width : 0;
        const target = ratio * maxScrollLeft;
        const el = activeHScrollRef.current || getHorizontalScrollEl();
        if (el) {
          el.scrollLeft = target;
          syncTableScrollState(el);
        }
      };

      if (!canScroll) return null;
      return (
        <div
          className="anima-tabla__table-progress-wrap"
          style={{
            width: '100%',
            minWidth: 0,
            flexShrink: 0,
        marginTop: '1.5rem',
            boxSizing: 'border-box',
          }}
        >
          <div
            style={{
              position: 'relative',
              width: '100%',
              height: '10px',
              borderRadius: '9999px',
              backgroundColor: TABLE_PROGRESS_TRACK_COLOR,
              overflow: 'hidden',
            }}
            aria-hidden="true"
            onMouseDown={(e) => {
              if (e.button !== 0) return;
              scrollToTrackPosition(e.clientX, e.currentTarget);
            }}
          >
            <div
              style={{
                position: 'absolute',
                top: 0,
                left: `${thumbLeftPct}%`,
                width: `${thumbWidthPct}%`,
                height: '100%',
                borderRadius: '9999px',
                backgroundColor: TABLE_PROGRESS_FILL_COLOR,
                transition: isDraggingTableScroll ? 'none' : 'left 0.1s ease, width 0.1s ease',
              }}
              onMouseDown={(e) => {
                if (e.button !== 0) return;
                if (!canScroll) return;
                e.stopPropagation();
                setIsDraggingTableScroll(true);
                const startX = e.clientX;
                const scrollElForDrag = activeHScrollRef.current || getHorizontalScrollEl();
                const startLeft = scrollElForDrag ? scrollElForDrag.scrollLeft : 0;
                const track = e.currentTarget.parentElement;
                if (!track) return;
                const trackRect = track.getBoundingClientRect();
                const trackWidth = trackRect.width;
                const thumbWidthPx = (thumbWidthPct / 100) * trackWidth;
                const travelPx = Math.max(1, trackWidth - thumbWidthPx);
                const startThumbLeftPx = maxScrollLeft > 0 ? (startLeft / maxScrollLeft) * travelPx : 0;
                const pointerOffsetInsideThumb = startX - (trackRect.left + startThumbLeftPx);
                const initialUserSelect = document.body.style.userSelect;
                document.body.style.userSelect = 'none';
                const onMove = (moveEvt) => {
                  if (trackWidth <= 0 || maxScrollLeft <= 0) return;
                  const el = activeHScrollRef.current || getHorizontalScrollEl();
                  if (!el) return;
                  const rawThumbLeftPx = moveEvt.clientX - trackRect.left - pointerOffsetInsideThumb;
                  const thumbLeftPx = Math.max(0, Math.min(travelPx, rawThumbLeftPx));
                  const nextScrollLeft = (thumbLeftPx / travelPx) * maxScrollLeft;
                  el.scrollLeft = nextScrollLeft;
                  syncTableScrollState(el);
                };
                const onUp = () => {
                  setIsDraggingTableScroll(false);
                  document.body.style.userSelect = initialUserSelect;
                  document.removeEventListener('mousemove', onMove);
                  document.removeEventListener('mouseup', onUp);
                };
                document.addEventListener('mousemove', onMove);
                document.addEventListener('mouseup', onUp);
              }}
            />
          </div>
        </div>
      );
    })()
  );

  return (
    <div
      className={`anima-tabla__container ${containerClassName} ${className}`}
      style={{
        width: '100%',
        minWidth: 0,
        minHeight: isMobile ? 0 : '289px',
        height: 'auto',
        backgroundColor: 'transparent',
        boxSizing: 'border-box',
        display: 'flex',
        flexDirection: 'column',
        overflow: 'hidden',
        ...(isMobile && { flex: '0 0 auto', alignSelf: 'flex-start' }),
        ...(containerStyle || {}),
      }}
      {...props}
    >
      <style>{TABLA_GRID_INLINE_STYLES}</style>
      <TablaSearch
        searchValue={searchValue}
        onSearchChange={onSearchChange}
        searchPlaceholder={
          searchPlaceholder ?? t('table.searchPlaceholder', { defaultValue: "Buscar dentro de 'Usuarios'" })
        }
        wrapStyle={{
          ...(isIntermediateWidth && !isMobile && { marginBottom: 0 }),
        }}
        trailing={
          <>
        {(showFiltersButton || typeof onFiltersClick === 'function') && (
          <button
            type="button"
            onClick={handleFiltersButtonClick}
            aria-label={filtersToolbarDisplayLabel}
            title={undefined}
            style={{
              flexShrink: 0,
              height: 40,
              width: 'auto',
              minWidth: 102,
              paddingTop: 8,
              paddingRight: 16,
              paddingBottom: 8,
              paddingLeft: 16,
              gap: showFiltersButtonIcon ? 8 : 0,
              borderRadius: 24,
              border: hasActiveFilters
                ? `1px solid ${TABLA_FILTERS_ACTIVE_COLOR}`
                : '1px solid var(--color-grey-600, #5F5F5F)',
              background: hasActiveFilters ? TABLA_FILTERS_ACTIVE_COLOR : '#FFFFFF',
              color: hasActiveFilters ? '#FFFFFF' : TABLA_FILTERS_ICON_COLOR,
              display: 'inline-flex',
              alignItems: 'center',
              justifyContent: 'center',
              cursor: 'pointer',
              boxSizing: 'border-box',
              opacity: 1,
            }}
          >
            {showFiltersButtonIcon && (
              <Icon
                name="AdjustmentsHorizontalIcon"
                variant="24-outline"
                size={16}
                style={{
                  flexShrink: 0,
                  width: 16,
                  height: 16,
                  color: hasActiveFilters ? '#FFFFFF' : TABLA_FILTERS_ICON_COLOR,
                }}
              />
            )}
            <span
              style={{
                fontFamily: FONT_FAMILY_SANS,
                fontWeight: 400,
                fontSize: 16,
                lineHeight: '24px',
                letterSpacing: '0px',
                color: hasActiveFilters ? '#FFFFFF' : TABLA_FILTERS_ICON_COLOR,
                whiteSpace: 'nowrap',
                verticalAlign: 'middle',
              }}
            >
              {filtersToolbarDisplayLabel}
            </span>
          </button>
        )}
        {Array.isArray(toolbarIcons) && toolbarIcons.length > 0 && (
          <div
            className="anima-tabla__toolbar-icons"
            style={{
              flexShrink: 0,
              marginLeft: 'auto',
              display: 'flex',
              alignItems: 'center',
              gap: '12px',
              boxSizing: 'border-box',
            }}
          >
            {toolbarIcons.map((item, index) => {
              const { iconName, onClick, ariaLabel, href, selected } = item ?? {};
              if (!iconName) return null;
              const isDownloadToolbarIcon = iconName === 'ArrowDownTrayIcon';
              const isLastTwo = index >= toolbarIcons.length - 2;
              const isLink = typeof href === 'string' && href.length > 0;
              const Wrapper = isLink ? 'a' : (onClick ? 'button' : 'div');
              const style = {
                width: 48,
                height: 40,
                paddingTop: 8,
                paddingRight: 12,
                paddingBottom: 8,
                paddingLeft: 12,
                gap: 12,
                borderRadius: 8,
                opacity: 1,
                boxSizing: 'border-box',
                display: 'flex',
                alignItems: 'center',
                justifyContent: 'center',
                border: 'none',
                background: 'transparent',
                cursor: isLink || onClick ? 'pointer' : 'default',
                textDecoration: 'none',
                color: 'inherit',
              };
              const btnClass = [
                'anima-tabla__toolbar-btn',
                isLastTwo ? 'anima-tabla__toolbar-btn--with-border' : '',
                selected ? 'anima-tabla__toolbar-btn--selected' : '',
              ].filter(Boolean).join(' ');
              return (
                <Wrapper
                  key={index}
                  type={Wrapper === 'button' ? 'button' : undefined}
                  href={Wrapper === 'a' ? href : undefined}
                  onClick={onClick}
                  aria-label={ariaLabel}
                  className={btnClass}
                  style={style}
                >
                  <Icon
                    name={iconName}
                    variant="24-outline"
                    size={24}
                    className="anima-tabla__toolbar-btn-icon"
                    style={{
                      flexShrink: 0,
                      color: isDownloadToolbarIcon
                        ? TABLA_TOOLBAR_DOWNLOAD_ICON_COLOR
                        : 'var(--color-grey-600, #5F5F5F)',
                    }}
                  />
                </Wrapper>
              );
            })}
          </div>
        )}
          </>
        }
      />
      {(filtersArea != null || hasAppliedFilters) && (
        <div
          className={`anima-tabla__filters-area ${filtersAreaContainerClassName}`}
          style={{
            width: '100%',
            minWidth: 0,
            marginTop: '16px',
            marginBottom: '8px',
            boxSizing: 'border-box',
            ...(filtersAreaContainerStyle || {}),
          }}
        >
          {filtersArea ?? (
            <AppliedFilters
              appliedFilters={appliedFilters}
              onAppliedFilterRemove={onAppliedFilterRemove}
              onAppliedFiltersClear={onAppliedFiltersClear}
              showAppliedFiltersLabel={showAppliedFiltersLabel}
              appliedFiltersLabel={appliedFiltersLabel}
              appliedFiltersClearLabel={appliedFiltersClearLabel}
            />
          )}
        </div>
      )}
      <FiltrosAvanzados
        isOpen={isAdvancedFiltersModalOpen}
        onClose={() => setIsAdvancedFiltersModalOpen(false)}
        title={advancedFiltersModalTitle ?? t('table.advancedFiltersTitle', { defaultValue: 'Filtros avanzados' })}
        size={advancedFiltersModalSize}
        footer={advancedFiltersModalFooter}
        applyLabel={advancedFiltersApplyLabel}
        cancelLabel={advancedFiltersCancelLabel}
        onCancel={onAdvancedFiltersCancel}
        {...(advancedFiltersModalProps || {})}
        onApply={handleAdvancedFiltersApply}
        onClear={handleAdvancedFiltersClear}
      >
        {advancedFiltersModalContent}
      </FiltrosAvanzados>
      <div
        className="anima-tabla__middle"
        style={{
          width: '100%',
          minWidth: 0,
          flex: isMobile ? '0 0 auto' : '1 1 auto',
          minHeight: 0,
          display: 'flex',
          flexDirection: 'column',
          boxSizing: 'border-box',
          ...(isMobile && { gap: 0, padding: 0, margin: 0 }),
        }}
      >
        {debugExternalVerticalScroll && showExternalVerticalScroll && (
          <div
            style={{
              width: '100%',
              marginTop: '6px',
              marginBottom: '6px',
              fontFamily: FONT_FAMILY_MONO,
              fontSize: '12px',
              lineHeight: '18px',
              color: '#5F5F5F',
            }}
          >
            {`vScroll debug -> top:${Math.round(verticalScrollState.scrollTop)} | clientH:${Math.round(verticalScrollState.clientHeight)} | scrollH:${Math.round(verticalScrollState.scrollHeight)}`}
          </div>
        )}
        <div
          className="anima-tabla__middle-spacer"
          style={{
            flex: isMobile ? '0 0 auto' : 1,
            minHeight: 0,
            ...(isMobile && { order: 1 }),
            ...(isIntermediateWidth && !isMobile && {
              flex: '0 0 12px',
              minHeight: '12px',
              height: '12px',
              maxHeight: '12px',
              overflow: 'hidden',
            }),
          }}
        />
        <div
          className="anima-tabla__red-bar"
          style={{
            width: '100%',
            flexShrink: 0,
            height: '21px',
            minHeight: '21px',
            boxSizing: 'border-box',
            display: 'flex',
            alignItems: 'stretch',
            flexWrap: 'nowrap',
            ...(isMobile && { order: 2 }),
            ...(isIntermediateWidth && !isMobile && { marginTop: 0, marginBottom: 0 }),
          }}
        >
          <div
            className="anima-tabla__red-bar-registros"
            style={{
              flex: 1,
              minWidth: 0,
              display: 'flex',
              alignItems: 'center',
              paddingLeft: '8px',
              overflow: 'hidden',
            }}
          >
            {tableLoading ? (
              <Skeleton variant="rectangular" height={21} width={140} />
            ) : (
              showSelectionClusterInRedBar ? (
                <div
                  style={{
                    display: 'flex',
                    alignItems: 'center',
                    flexWrap: 'wrap',
                    gap: '20px',
                    minWidth: 0,
                  }}
                >
                  <span
                    style={{
                      ...RED_BAR_RECORDS_TEXT_STYLE,
                      display: 'inline-flex',
                      alignItems: 'center',
                      /* Un espacio literal entre hijos flex colapsa; el gap separa número y etiqueta. */
                      gap: '0.4em',
                    }}
                  >
                    <span style={{ fontWeight: 700 }}>{selectedCount}</span>
                    <span>
                      {selectedCount === 1
                        ? t('table.selectedLabelOne', { defaultValue: 'seleccionada' })
                        : t('table.selectedLabelMany', { defaultValue: 'seleccionadas' })}
                    </span>
                  </span>
                  <div
                    style={{
                      display: 'flex',
                      alignItems: 'center',
                      gap: '8px',
                      flexWrap: 'wrap',
                      flexShrink: 0,
                    }}
                  >
                    <button
                      type="button"
                      style={RED_BAR_BULK_ACTION_BUTTON_STYLE}
                      onClick={() => onBulkEditClick?.()}
                      aria-label={t('table.bulkEdit', { defaultValue: 'Editar en masa' })}
                    >
                      <Icon
                        name="PencilIcon"
                        size={13}
                        aria-hidden
                        style={{ flexShrink: 0, color: 'currentColor' }}
                      />
                      {t('table.bulkEdit', { defaultValue: 'Editar en masa' })}
                    </button>
                    <span aria-hidden style={RED_BAR_BULK_ACTION_SEPARATOR_STYLE} />
                    <button
                      type="button"
                      style={RED_BAR_BULK_ACTION_BUTTON_STYLE}
                      onClick={() => onBulkArchiveClick?.()}
                      aria-label={t('table.bulkArchive', { defaultValue: 'Archivar' })}
                    >
                      <Icon
                        name="ArchiveBoxIcon"
                        size={13}
                        aria-hidden
                        style={{ flexShrink: 0, color: 'currentColor' }}
                      />
                      {t('table.bulkArchive', { defaultValue: 'Archivar' })}
                    </button>
                    <span aria-hidden style={RED_BAR_BULK_ACTION_SEPARATOR_STYLE} />
                    <button
                      type="button"
                      style={RED_BAR_BULK_ACTION_BUTTON_STYLE}
                      onClick={() => onBulkExportClick?.()}
                      aria-label={t('table.bulkExport', { defaultValue: 'Exportar' })}
                    >
                      <Icon
                        name="ArrowUpTrayIcon"
                        size={13}
                        aria-hidden
                        style={{ flexShrink: 0, color: 'currentColor' }}
                      />
                      {t('table.bulkExport', { defaultValue: 'Exportar' })}
                    </button>
                  </div>
                </div>
              ) : (
                <span style={RED_BAR_RECORDS_TEXT_STYLE}>
                  {t('table.recordsPrefix', { defaultValue: 'Registros:' })}{' '}
                  {typeof onRegistrosPorPaginaCommit === 'function' ? (
                    <RegistrosHastaInput
                      registrosPorPagina={registrosPorPaginaLabel}
                      totalRegistrosLabel={totalRegistrosLabel}
                      onCommit={onRegistrosPorPaginaCommit}
                      disabled={tableLoading || sinContenido}
                    />
                  ) : (
                    <span style={RED_BAR_RECORDS_RANGE_NUMBER_STYLE}>{registrosPorPaginaLabel}</span>
                  )}
                  {' '}
                  {t('table.of', { defaultValue: 'de' })}{' '}
                  {totalRegistrosLabel}
                </span>
              )
            )}
          </div>
          <div
            className="anima-tabla__red-bar-pagination"
            style={{
              width: 'auto',
              flexShrink: 0,
              flex: '0 0 auto',
              boxSizing: 'border-box',
              display: 'flex',
              alignItems: 'center',
              justifyContent: 'flex-end',
              paddingRight: '8px',
              gap: '8px',
              minHeight: '21px',
            }}
          >
            {tableLoading ? (
              <Skeleton variant="rectangular" height={21} width={100} />
            ) : (
              <>
                {showDuplicateRegistrosBesidePage && (
                  <>
                    <div style={{ display: 'flex', alignItems: 'center', overflow: 'hidden' }}>
                      <span style={RED_BAR_RECORDS_TEXT_STYLE}>
                        {t('table.recordsPrefix', { defaultValue: 'Registros:' })}{' '}
                        {typeof onRegistrosPorPaginaCommit === 'function' ? (
                          <RegistrosHastaInput
                            registrosPorPagina={registrosPorPaginaLabel}
                            totalRegistrosLabel={totalRegistrosLabel}
                            onCommit={onRegistrosPorPaginaCommit}
                            disabled={tableLoading || sinContenido}
                          />
                        ) : (
                          <span style={RED_BAR_RECORDS_RANGE_NUMBER_STYLE}>{registrosPorPaginaLabel}</span>
                        )}
                        {' '}
                        {t('table.of', { defaultValue: 'de' })}{' '}
                        {totalRegistrosLabel}
                      </span>
                    </div>
                    <span
                      aria-hidden
                      style={{
                        color: 'var(--grey-600, #5F5F5F)',
                        fontFamily: FONT_FAMILY_MONO,
                        fontWeight: 500,
                        fontSize: '14px',
                        lineHeight: '21px',
                        flexShrink: 0,
                        alignSelf: 'center',
                        userSelect: 'none',
                      }}
                    >
                      |
                    </span>
                  </>
                )}
                <div className="anima-tabla__red-bar-pagina-text" style={{ display: 'flex', alignItems: 'center' }}>
                  <span
                    style={{
                      fontFamily: FONT_FAMILY_MONO,
                      fontWeight: 500,
                      fontSize: '14px',
                      lineHeight: '21px',
                      letterSpacing: '0.01em',
                      color: 'var(--grey-600, #5F5F5F)',
                      whiteSpace: 'nowrap',
                    }}
                  >
                    {t('table.pagePrefix', { defaultValue: 'Página' })}{' '}
                    <span style={{ fontWeight: 700 }}>{currentPage}</span>
                    {` ${t('table.of', { defaultValue: 'de' })} ${totalPages}`}
                  </span>
                </div>
              </>
            )}
            {!tableLoading && (
              <>
                <div className="anima-tabla__red-bar-pagination-buttons" style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
                  <button
                    type="button"
                    disabled={!hasPrevPage}
                    onClick={onPrevPage}
                    aria-label={t('table.prevPageAria', { defaultValue: 'Página anterior' })}
                    style={{
                      padding: 0,
                      border: 'none',
                      background: 'none',
                      cursor: hasPrevPage ? 'pointer' : 'default',
                      display: 'flex',
                      alignItems: 'center',
                      justifyContent: 'center',
                    }}
                  >
                    <Icon
                      name="ChevronDoubleLeftIcon"
                      size={14}
                      style={{
                        width: 12,
                        height: 14,
                        opacity: 1,
                        color: hasPrevPage ? 'var(--color-main-700, #38656D)' : 'var(--color-main-400, #84A0A5)',
                      }}
                    />
                  </button>
                  <button
                    type="button"
                    disabled={!hasNextPage}
                    onClick={onNextPage}
                    aria-label={t('table.nextPageAria', { defaultValue: 'Página siguiente' })}
                    style={{
                      padding: 0,
                      border: 'none',
                      background: 'none',
                      cursor: hasNextPage ? 'pointer' : 'default',
                      display: 'flex',
                      alignItems: 'center',
                      justifyContent: 'center',
                    }}
                  >
                    <Icon
                      name="ChevronDoubleRightIcon"
                      size={14}
                      style={{
                        width: 12,
                        height: 14,
                        opacity: 1,
                        color: hasNextPage ? 'var(--color-main-700, #38656D)' : 'var(--color-main-400, #84A0A5)',
                      }}
                    />
                  </button>
                </div>
              </>
            )}
          </div>
        </div>
        {showNarrowTableBulkRow && (
          <div
            className="anima-tabla__narrow-bulk-row"
            style={{
              width: '100%',
              boxSizing: 'border-box',
              display: 'flex',
              alignItems: 'center',
              justifyContent: 'space-between',
              gap: '12px',
              paddingLeft: '8px',
              paddingRight: '8px',
              paddingTop: '12px',
              paddingBottom: '8px',
              flexShrink: 0,
            }}
          >
            <span
              style={{
                ...RED_BAR_RECORDS_TEXT_STYLE,
                display: 'inline-flex',
                alignItems: 'center',
                gap: '0.35em',
                minWidth: 0,
              }}
            >
              <span style={{ fontWeight: 700 }}>{selectedCount}</span>
              <span>
                {selectedCount === 1
                  ? t('table.selectedLabelOne', { defaultValue: 'seleccionada' })
                  : t('table.selectedLabelMany', { defaultValue: 'seleccionadas' })}
              </span>
            </span>
            <div
              style={{
                position: 'relative',
                flex: '0 1 auto',
                minWidth: 0,
                maxWidth: 'min(100%, 220px)',
              }}
            >
              <select
                aria-label={t('table.bulkActionsSelectAria', { defaultValue: 'Acciones en masa' })}
                defaultValue="edit"
                onChange={handleNarrowBulkSelectChange}
                style={{
                  width: '100%',
                  minWidth: 140,
                  fontFamily: FONT_FAMILY_MONO,
                  fontWeight: 500,
                  fontSize: '14px',
                  lineHeight: '21px',
                  letterSpacing: '0.01em',
                  color: 'var(--color-main-700, #38656D)',
                  border: '1px solid var(--color-main-700, #38656D)',
                  borderRadius: '8px',
                  paddingTop: '8px',
                  paddingRight: '36px',
                  paddingBottom: '8px',
                  paddingLeft: '12px',
                  backgroundColor: 'var(--color-neutrals-white, #FFFFFF)',
                  appearance: 'none',
                  WebkitAppearance: 'none',
                  MozAppearance: 'none',
                  cursor: 'pointer',
                  boxSizing: 'border-box',
                }}
              >
                <option value="edit">{t('table.bulkEdit', { defaultValue: 'Editar en masa' })}</option>
                <option value="archive">{t('table.bulkArchive', { defaultValue: 'Archivar' })}</option>
                <option value="export">{t('table.bulkExport', { defaultValue: 'Exportar' })}</option>
              </select>
              <Icon
                name="ChevronDownIcon"
                variant="20-solid"
                size={20}
                aria-hidden
                style={{
                  position: 'absolute',
                  right: '10px',
                  top: '50%',
                  transform: 'translateY(-50%)',
                  pointerEvents: 'none',
                  color: 'var(--color-main-700, #38656D)',
                }}
              />
            </div>
          </div>
        )}
        {isMobile ? (
          <>
            <div
              className={middleTableWrapClassName}
              style={{
                flexShrink: 0,
                width: '100%',
                margin: 0,
                padding: 0,
              }}
            >
              {tableArea != null ? tableArea : renderTableContent()}
            </div>
            {tableArea == null && renderTableProgressBar()}
            <div
              className="anima-tabla__middle-content-wrap"
              style={{
                flex: '0 0 auto',
                minHeight: 0,
                display: 'flex',
                flexDirection: 'column',
                boxSizing: 'border-box',
              }}
            >
              {children != null && (
                <div
                  className={`anima-tabla__content ${contentContainerClassName}`}
                  style={{
                    width: '100%',
                    minWidth: 0,
                    flex: 1,
                    paddingTop: 0,
                    paddingRight: 'var(--space-16, 1rem)',
                    paddingBottom: 0,
                    paddingLeft: 'var(--space-16, 1rem)',
                    boxSizing: 'border-box',
                    display: 'flex',
                    flexDirection: 'column',
                    gap: '24px',
                    overflow: 'auto',
                    ...(contentContainerStyle || {}),
                  }}
                >
                  {children}
                </div>
              )}
            </div>
          </>
        ) : (
          <>
            <div
              className="anima-tabla__middle-content-wrap"
              style={{
                flex: 1,
                minHeight: 0,
                display: 'flex',
                flexDirection: 'column',
                boxSizing: 'border-box',
              }}
            >
              {children != null && (
                <div
                  className={`anima-tabla__content ${contentContainerClassName}`}
                  style={{
                    width: '100%',
                    minWidth: 0,
                    flex: 1,
                    paddingTop: '16px',
                    paddingRight: 'var(--space-16, 1rem)',
                    paddingBottom: '24px',
                    paddingLeft: 'var(--space-16, 1rem)',
                    boxSizing: 'border-box',
                    display: 'flex',
                    flexDirection: 'column',
                    gap: '24px',
                    overflow: 'auto',
                    ...(contentContainerStyle || {}),
                  }}
                >
                  {children}
                </div>
              )}
            </div>
            <div
              className={middleTableWrapClassName}
              style={{
                flexShrink: 0,
                width: '100%',
                ...(isIntermediateWidth && { marginTop: '12px' }),
              }}
            >
              {tableArea != null ? tableArea : renderTableContent()}
            </div>
            {tableArea == null && renderTableProgressBar()}
          </>
        )}
      </div>
    </div>
  );
};

export default Tabla;
export {
  DEFAULT_TABLE_COLUMNS as DEFAULT_TABLA_COLUMNS,
  DEFAULT_TABLE_ROWS as DEFAULT_TABLA_ROWS,
  TABLA_TOOLBAR_ICONS_DEFAULT,
};
