import 'react-data-grid/lib/styles.css';
import { css, cx } from '@emotion/css';
import { Property } from 'csstype';
import { Key, ReactNode, useCallback, useLayoutEffect, useMemo, useRef, useState } from 'react';
import {
  Cell,
  CellRendererProps,
  DataGrid,
  DataGridHandle,
  DataGridProps,
  RenderCellProps,
  RenderRowProps,
  Row,
  SortColumn,
} from 'react-data-grid';

import {
  DataHoverClearEvent,
  DataHoverEvent,
  FALLBACK_COLOR,
  Field,
  FieldType,
  getDisplayProcessor,
  GrafanaTheme2,
  ReducerID,
} from '@grafana/data';
import { t, Trans } from '@grafana/i18n';
import { FieldColorModeId, TableCellHeight } from '@grafana/schema';

import { useStyles2, useTheme2 } from '../../../themes/ThemeContext';
import { ContextMenu } from '../../ContextMenu/ContextMenu';
import { MenuItem } from '../../Menu/MenuItem';
import { Pagination } from '../../Pagination/Pagination';
import { PanelContext, usePanelContext } from '../../PanelChrome';
import { DataLinksActionsTooltip } from '../DataLinksActionsTooltip';
import { TableCellInspector, TableCellInspectorMode } from '../TableCellInspector';
import { CellColors, TableCellDisplayMode } from '../types';
import { DataLinksActionsTooltipState } from '../utils';

import { HeaderCell } from './Cells/HeaderCell';
import { RowExpander } from './Cells/RowExpander';
import { TableCellActions } from './Cells/TableCellActions';
import { getCellRenderer } from './Cells/renderers';
import { COLUMN, TABLE } from './constants';
import {
  useColumnResize,
  useFilteredRows,
  useFooterCalcs,
  useHeaderHeight,
  usePaginatedRows,
  useRowHeight,
  useScrollbarWidth,
  useSortedRows,
  useTypographyCtx,
} from './hooks';
import { TableNGProps, TableRow, TableSummaryRow, TableColumn, ContextMenuProps } from './types';
import {
  frameToRecords,
  getDefaultRowHeight,
  getDisplayName,
  getIsNestedTable,
  getTextAlign,
  getVisibleFields,
  shouldTextOverflow,
  getApplyToRowBgFn,
  computeColWidths,
  applySort,
  getCellColors,
  getCellOptions,
  shouldTextWrap,
  isCellInspectEnabled,
  getCellLinks,
  withDataLinksActionsTooltip,
} from './utils';

type CellRootRenderer = (key: React.Key, props: CellRendererProps<TableRow, TableSummaryRow>) => React.ReactNode;

export function TableNG(props: TableNGProps) {
  const {
    cellHeight,
    data,
    enablePagination = false,
    enableSharedCrosshair = false,
    enableVirtualization,
    footerOptions,
    getActions = () => [],
    height,
    initialSortBy,
    noHeader,
    onCellFilterAdded,
    onColumnResize,
    onSortByChange,
    showTypeIcons,
    structureRev,
    transparent,
    width,
  } = props;

  const theme = useTheme2();
  const styles = useStyles2(getGridStyles, {
    enablePagination,
    transparent,
  });
  const panelContext = usePanelContext();

  const getCellActions = useCallback(
    (field: Field, rowIdx: number) => getActions(data, field, rowIdx),
    [getActions, data]
  );

  const hasHeader = !noHeader;
  const hasFooter = Boolean(footerOptions?.show && footerOptions.reducer?.length);
  const isCountRowsSet = Boolean(
    footerOptions?.countRows &&
      footerOptions.reducer &&
      footerOptions.reducer.length &&
      footerOptions.reducer[0] === ReducerID.count
  );

  const [contextMenuProps, setContextMenuProps] = useState<ContextMenuProps | null>(null);
  const [isContextMenuOpen, setIsContextMenuOpen] = useState(false);

  const resizeHandler = useColumnResize(onColumnResize);

  useLayoutEffect(() => {
    if (!isContextMenuOpen) {
      return;
    }

    function onClick(_event: MouseEvent) {
      setIsContextMenuOpen(false);
    }

    window.addEventListener('click', onClick);

    return () => {
      window.removeEventListener('click', onClick);
    };
  }, [isContextMenuOpen]);

  const rows = useMemo(() => frameToRecords(data), [data]);
  const hasNestedFrames = useMemo(() => getIsNestedTable(data.fields), [data]);

  const {
    rows: filteredRows,
    filter,
    setFilter,
    crossFilterOrder,
    crossFilterRows,
  } = useFilteredRows(rows, data.fields, { hasNestedFrames });

  const {
    rows: sortedRows,
    sortColumns,
    setSortColumns,
  } = useSortedRows(filteredRows, data.fields, { hasNestedFrames, initialSortBy });

  const defaultRowHeight = getDefaultRowHeight(theme, cellHeight);
  const defaultHeaderHeight = getDefaultRowHeight(theme, TableCellHeight.Sm);
  const [isInspecting, setIsInspecting] = useState(false);
  const [expandedRows, setExpandedRows] = useState<Record<string, boolean>>({});

  // vt scrollbar accounting for column auto-sizing
  const visibleFields = useMemo(() => getVisibleFields(data.fields), [data.fields]);
  const gridRef = useRef<DataGridHandle>(null);
  const scrollbarWidth = useScrollbarWidth(gridRef, height, sortedRows);
  const availableWidth = useMemo(
    () => (hasNestedFrames ? width - COLUMN.EXPANDER_WIDTH : width) - scrollbarWidth,
    [width, hasNestedFrames, scrollbarWidth]
  );
  const typographyCtx = useTypographyCtx();
  const widths = useMemo(() => computeColWidths(visibleFields, availableWidth), [visibleFields, availableWidth]);
  const headerHeight = useHeaderHeight({
    columnWidths: widths,
    fields: visibleFields,
    enabled: hasHeader,
    defaultHeight: defaultHeaderHeight,
    sortColumns,
    showTypeIcons: showTypeIcons ?? false,
    typographyCtx,
  });
  const rowHeight = useRowHeight({
    columnWidths: widths,
    fields: visibleFields,
    hasNestedFrames,
    defaultHeight: defaultRowHeight,
    headerHeight,
    expandedRows,
    typographyCtx,
  });

  const {
    rows: paginatedRows,
    page,
    setPage,
    numPages,
    pageRangeStart,
    pageRangeEnd,
    smallPagination,
  } = usePaginatedRows(sortedRows, {
    enabled: enablePagination,
    width: availableWidth,
    height,
    headerHeight,
    footerHeight: hasFooter ? defaultRowHeight : 0,
    rowHeight,
  });

  // Create a map of column key to text wrap
  const footerCalcs = useFooterCalcs(sortedRows, data.fields, { enabled: hasFooter, footerOptions, isCountRowsSet });
  const applyToRowBgFn = useMemo(() => getApplyToRowBgFn(data.fields, theme) ?? undefined, [data.fields, theme]);

  const renderRow = useMemo(
    () => renderRowFactory(data.fields, panelContext, expandedRows, enableSharedCrosshair),
    [data, enableSharedCrosshair, expandedRows, panelContext]
  );

  const commonDataGridProps = useMemo(
    () =>
      ({
        enableVirtualization,
        defaultColumnOptions: {
          minWidth: 50,
          resizable: true,
          sortable: true,
          // draggable: true,
        },
        onCellContextMenu: ({ row, column }, event) => {
          // in nested tables, it's possible for this event to trigger in a column header
          // when holding Ctrl for multi-row sort.
          if (column.key === 'expanded') {
            return;
          }

          event.preventGridDefault();
          // Do not show the default context menu
          event.preventDefault();

          const cellValue = row[column.key];
          setContextMenuProps({
            // rowIdx: rows.indexOf(row),
            value: String(cellValue ?? ''),
            top: event.clientY,
            left: event.clientX,
          });

          setIsContextMenuOpen(true);
        },
        onColumnResize: resizeHandler,
        onSortColumnsChange: (newSortColumns: SortColumn[]) => {
          setSortColumns(newSortColumns);
          onSortByChange?.(
            newSortColumns.map(({ columnKey, direction }) => ({
              displayName: columnKey,
              desc: direction === 'DESC',
            }))
          );
        },
        sortColumns,
        rowHeight,
        bottomSummaryRows: hasFooter ? [{}] : undefined,
      }) satisfies Partial<DataGridProps<TableRow, TableSummaryRow>>,
    [enableVirtualization, resizeHandler, sortColumns, rowHeight, hasFooter, setSortColumns, onSortByChange]
  );

  interface Schema {
    columns: TableColumn[];
    cellRootRenderers: Record<string, CellRootRenderer>;
    colsWithTooltip: Record<string, boolean>;
  }

  const { columns, cellRootRenderers, colsWithTooltip } = useMemo(() => {
    const fromFields = (f: Field[], widths: number[]) => {
      const result: Schema = {
        columns: [],
        cellRootRenderers: {},
        colsWithTooltip: {},
      };

      let lastRowIdx = -1;
      let _rowHeight = 0;

      f.forEach((field, i) => {
        const cellOptions = getCellOptions(field);
        const cellType = cellOptions.type;

        // make sure we use mappings exclusively if they exist, ignore default thresholds mode
        // we hack this by using the single color mode calculator
        if (cellType === TableCellDisplayMode.Pill && (field.config.mappings?.length ?? 0 > 0)) {
          field = {
            ...field,
            config: {
              ...field.config,
              color: {
                ...field.config.color,
                mode: FieldColorModeId.Fixed,
                fixedColor: field.config.color?.fixedColor ?? FALLBACK_COLOR,
              },
            },
          };
          field.display = getDisplayProcessor({ field, theme });
        }

        const justifyContent = getTextAlign(field);
        const footerStyles = getFooterStyles(justifyContent);
        const displayName = getDisplayName(field);
        const headerCellClass = getHeaderCellStyles(theme, justifyContent).headerCell;
        const renderFieldCell = getCellRenderer(field, cellOptions);

        const cellInspect = isCellInspectEnabled(field);
        const showFilters = Boolean(field.config.filterable && onCellFilterAdded != null);
        const showActions = cellInspect || showFilters;
        const width = widths[i];

        // helps us avoid string cx and emotion per-cell
        const cellActionClassName = showActions
          ? cx(
              'table-cell-actions',
              styles.cellActions,
              justifyContent === 'flex-end' ? styles.cellActionsEnd : styles.cellActionsStart
            )
          : undefined;

        const shouldOverflow = shouldTextOverflow(field);
        const shouldWrap = shouldTextWrap(field);
        const withTooltip = withDataLinksActionsTooltip(field, cellType);

        result.colsWithTooltip[displayName] = withTooltip;

        // this fires first
        const renderCellRoot = (key: Key, props: CellRendererProps<TableRow, TableSummaryRow>): ReactNode => {
          const rowIdx = props.row.__index;
          const value = props.row[props.column.key];

          // meh, this should be cached by the renderRow() call?
          if (rowIdx !== lastRowIdx) {
            _rowHeight = typeof rowHeight === 'function' ? rowHeight(props.row) : rowHeight;
            lastRowIdx = rowIdx;
          }

          let colors: CellColors;

          if (applyToRowBgFn != null) {
            colors = applyToRowBgFn(props.rowIdx);
          } else if (cellType !== TableCellDisplayMode.Auto) {
            const displayValue = field.display!(value); // this fires here to get colors, then again to get rendered value?
            colors = getCellColors(theme, cellOptions, displayValue);
          } else {
            colors = {};
          }

          const cellStyle = getCellStyles(theme, field, _rowHeight, shouldWrap, shouldOverflow, withTooltip, colors);

          return (
            <Cell
              key={key}
              {...props}
              className={cx(props.className, cellStyle.cell)}
              style={{ color: colors.textColor ?? 'inherit' }}
            />
          );
        };

        result.cellRootRenderers[displayName] = renderCellRoot;

        // this fires second
        const renderCellContent = (props: RenderCellProps<TableRow, TableSummaryRow>): JSX.Element => {
          const rowIdx = props.row.__index;
          const value = props.row[props.column.key];
          const frame = data;

          return (
            <>
              {renderFieldCell({
                cellOptions,
                frame,
                field,
                height: _rowHeight,
                justifyContent,
                rowIdx,
                theme,
                value,
                width,
                cellInspect,
                showFilters,
                getActions: getCellActions,
              })}
              {showActions && (
                <TableCellActions
                  field={field}
                  value={value}
                  cellOptions={cellOptions}
                  displayName={displayName}
                  cellInspect={cellInspect}
                  showFilters={showFilters}
                  className={cellActionClassName}
                  setIsInspecting={setIsInspecting}
                  setContextMenuProps={setContextMenuProps}
                  onCellFilterAdded={onCellFilterAdded}
                />
              )}
            </>
          );
        };

        const column: TableColumn = {
          field,
          key: displayName,
          name: displayName,
          width,
          headerCellClass,
          renderCell: renderCellContent,
          renderHeaderCell: ({ column, sortDirection }) => (
            <HeaderCell
              column={column}
              rows={rows}
              field={field}
              filter={filter}
              setFilter={setFilter}
              crossFilterOrder={crossFilterOrder}
              crossFilterRows={crossFilterRows}
              direction={sortDirection}
              showTypeIcons={showTypeIcons}
            />
          ),
          renderSummaryCell: () => {
            if (isCountRowsSet && i === 0) {
              return (
                <div className={footerStyles.footerCellCountRows}>
                  <span>
                    <Trans i18nKey="grafana-ui.table.count">Count</Trans>
                  </span>
                  <span>{footerCalcs[i]}</span>
                </div>
              );
            }
            return <div className={footerStyles.footerCell}>{footerCalcs[i]}</div>;
          },
        };

        result.columns.push(column);
      });

      return result;
    };

    const result = fromFields(visibleFields, widths);

    // handle nested frames rendering from here.
    if (!hasNestedFrames) {
      return result;
    }

    // pre-calculate renderRow and expandedColumns based on the first nested frame's fields.
    const firstNestedData = rows.find((r) => r.data)?.data;
    if (!firstNestedData) {
      return result;
    }

    const hasNestedHeaders = firstNestedData.meta?.custom?.noHeader !== true;
    const renderRow = renderRowFactory(firstNestedData.fields, panelContext, expandedRows, enableSharedCrosshair);
    const { columns: nestedColumns, cellRootRenderers: nestedCellRootRenderers } = fromFields(
      firstNestedData.fields,
      computeColWidths(firstNestedData.fields, availableWidth)
    );

    const renderCellRoot: CellRootRenderer = (key, props) => nestedCellRootRenderers[props.column.key](key, props);

    result.cellRootRenderers.expanded = (key, props) => <Cell key={key} {...props} />;

    // If we have nested frames, we need to add a column for the row expansion
    result.columns.unshift({
      key: 'expanded',
      name: '',
      field: {
        name: '',
        type: FieldType.other,
        config: {},
        values: [],
      },
      cellClass(row) {
        if (row.__depth !== 0) {
          return styles.cellNested;
        }
        return;
      },
      colSpan(args) {
        return args.type === 'ROW' && args.row.__depth === 1 ? data.fields.length : 1;
      },
      renderCell: ({ row }) => {
        if (row.__depth === 0) {
          return (
            <RowExpander
              height={defaultRowHeight}
              isExpanded={expandedRows[row.__index] ?? false}
              onCellExpand={() => {
                setExpandedRows({ ...expandedRows, [row.__index]: !expandedRows[row.__index] });
              }}
            />
          );
        }

        // Type guard to check if data exists as it's optional
        const nestedData = row.data;
        if (!nestedData) {
          return null;
        }

        const expandedRecords = applySort(frameToRecords(nestedData), nestedData.fields, sortColumns);
        if (!expandedRecords.length) {
          return (
            <div className={styles.noDataNested}>
              <Trans i18nKey="grafana-ui.table.nested-table.no-data">No data</Trans>
            </div>
          );
        }

        return (
          <DataGrid<TableRow, TableSummaryRow>
            {...commonDataGridProps}
            className={cx(styles.grid, styles.gridNested)}
            headerRowClass={cx(styles.headerRow, { [styles.displayNone]: !hasNestedHeaders })}
            headerRowHeight={hasNestedHeaders ? defaultHeaderHeight : 0}
            columns={nestedColumns}
            rows={expandedRecords}
            renderers={{ renderRow, renderCell: renderCellRoot }}
          />
        );
      },
      width: COLUMN.EXPANDER_WIDTH,
      minWidth: COLUMN.EXPANDER_WIDTH,
    });

    return result;
  }, [
    applyToRowBgFn,
    availableWidth,
    commonDataGridProps,
    crossFilterOrder,
    crossFilterRows,
    data,
    defaultHeaderHeight,
    defaultRowHeight,
    enableSharedCrosshair,
    expandedRows,
    filter,
    footerCalcs,
    hasNestedFrames,
    isCountRowsSet,
    onCellFilterAdded,
    panelContext,
    rowHeight,
    rows,
    setFilter,
    showTypeIcons,
    sortColumns,
    styles,
    theme,
    visibleFields,
    widths,
    getCellActions,
  ]);

  // invalidate columns on every structureRev change. this supports width editing in the fieldConfig.
  // eslint-disable-next-line react-hooks/exhaustive-deps
  const structureRevColumns = useMemo(() => columns, [columns, structureRev]);

  // we need to have variables with these exact names for the localization to work properly
  const itemsRangeStart = pageRangeStart;
  const displayedEnd = pageRangeEnd;
  const numRows = sortedRows.length;

  const renderCellRoot: CellRootRenderer = (key, props) => {
    return cellRootRenderers[props.column.key](key, props);
  };

  const [tooltipState, setTooltipState] = useState<DataLinksActionsTooltipState>();

  return (
    <>
      <DataGrid<TableRow, TableSummaryRow>
        {...commonDataGridProps}
        ref={gridRef}
        className={styles.grid}
        columns={structureRevColumns}
        rows={paginatedRows}
        headerRowClass={cx(styles.headerRow, { [styles.displayNone]: noHeader })}
        headerRowHeight={headerHeight}
        onCellClick={({ column, row }, { clientX, clientY, preventGridDefault }) => {
          // Note: could be column.field; JS says yes, but TS says no!
          const field = columns[column.idx].field;

          if (colsWithTooltip[getDisplayName(field)]) {
            const rowIdx = row.__index;
            setTooltipState({
              coords: {
                clientX,
                clientY,
              },
              links: getCellLinks(field, rowIdx),
              actions: getCellActions(field, rowIdx),
            });

            preventGridDefault();
          }
        }}
        onCellKeyDown={
          hasNestedFrames
            ? (_, event) => {
                if (event.isDefaultPrevented()) {
                  // skip parent grid keyboard navigation if nested grid handled it
                  event.preventGridDefault();
                }
              }
            : null
        }
        renderers={{ renderRow, renderCell: renderCellRoot }}
      />

      {enablePagination && (
        <div className={styles.paginationContainer}>
          <Pagination
            className="table-ng-pagination"
            currentPage={page + 1}
            numberOfPages={numPages}
            showSmallVersion={smallPagination}
            onNavigate={(toPage) => {
              setPage(toPage - 1);
            }}
          />
          {!smallPagination && (
            <div className={styles.paginationSummary}>
              {/* TODO: once TableRT is deprecated, we can update the localiziation
                    string with the more consistent variable names */}
              <Trans i18nKey="grafana-ui.table.pagination-summary">
                {{ itemsRangeStart }} - {{ displayedEnd }} of {{ numRows }} rows
              </Trans>
            </div>
          )}
        </div>
      )}

      {tooltipState && (
        <DataLinksActionsTooltip
          links={tooltipState.links ?? []}
          actions={tooltipState.actions}
          coords={tooltipState.coords}
          onTooltipClose={() => setTooltipState(undefined)}
        />
      )}

      {isContextMenuOpen && (
        <ContextMenu
          x={contextMenuProps?.left || 0}
          y={contextMenuProps?.top || 0}
          renderMenuItems={() => (
            <MenuItem
              label={t('grafana-ui.table.inspect-menu-label', 'Inspect value')}
              onClick={() => setIsInspecting(true)}
              className={styles.menuItem}
            />
          )}
          focusOnOpen={false}
        />
      )}

      {isInspecting && (
        <TableCellInspector
          mode={contextMenuProps?.mode ?? TableCellInspectorMode.text}
          value={contextMenuProps?.value}
          onDismiss={() => {
            setIsInspecting(false);
            setContextMenuProps(null);
          }}
        />
      )}
    </>
  );
}

/**
 * this is passed to the top-level `renderRow` prop on DataGrid. applies aria attributes and custom event handlers.
 */
const renderRowFactory =
  (
    fields: Field[],
    panelContext: PanelContext,
    expandedRows: Record<string, boolean>,
    enableSharedCrosshair: boolean
  ) =>
  (key: React.Key, props: RenderRowProps<TableRow, TableSummaryRow>): React.ReactNode => {
    const { row } = props;
    const rowIdx = row.__index;
    const isExpanded = !!expandedRows[rowIdx];

    // Don't render non expanded child rows
    if (row.__depth === 1 && !isExpanded) {
      return null;
    }

    // Add aria-expanded to parent rows that have nested data
    if (row.data) {
      return <Row key={key} {...props} aria-expanded={isExpanded} />;
    }

    const handlers: Partial<typeof props> = {};
    if (enableSharedCrosshair) {
      const timeField = fields.find((f) => f.type === FieldType.time);
      if (timeField) {
        handlers.onMouseEnter = () => {
          panelContext.eventBus.publish(
            new DataHoverEvent({
              point: {
                time: timeField?.values[rowIdx],
              },
            })
          );
        };
        handlers.onMouseLeave = () => {
          panelContext.eventBus.publish(new DataHoverClearEvent());
        };
      }
    }

    return <Row key={key} {...props} {...handlers} />;
  };

const getGridStyles = (
  theme: GrafanaTheme2,
  { enablePagination, transparent }: { enablePagination?: boolean; transparent?: boolean }
) => ({
  grid: css({
    '--rdg-background-color': transparent ? theme.colors.background.canvas : theme.colors.background.primary,
    '--rdg-header-background-color': transparent ? theme.colors.background.canvas : theme.colors.background.primary,
    '--rdg-border-color': theme.colors.border.weak,
    '--rdg-color': theme.colors.text.primary,

    // note: this cannot have any transparency since default cells that
    // overlay/overflow on hover inherit this background and need to occlude cells below
    '--rdg-row-background-color': transparent ? theme.colors.background.canvas : theme.colors.background.primary,
    '--rdg-row-hover-background-color': transparent
      ? theme.colors.background.primary
      : theme.colors.background.secondary,

    // TODO: magic 32px number is unfortunate. it would be better to have the content
    // flow using flexbox rather than hard-coding this size via a calc
    blockSize: enablePagination ? 'calc(100% - 32px)' : '100%',
    scrollbarWidth: 'thin',
    scrollbarColor: theme.isDark ? '#fff5 #fff1' : '#0005 #0001',

    border: 'none',

    '.rdg-summary-row': {
      '.rdg-cell': {
        zIndex: theme.zIndex.tooltip - 1,
        paddingInline: TABLE.CELL_PADDING,
        paddingBlock: TABLE.CELL_PADDING,
      },
    },
  }),
  gridNested: css({
    height: '100%',
    width: `calc(100% - ${COLUMN.EXPANDER_WIDTH - TABLE.CELL_PADDING * 2 - 1}px)`,
    overflow: 'visible',
    marginLeft: COLUMN.EXPANDER_WIDTH - TABLE.CELL_PADDING - 1,
    marginBlock: TABLE.CELL_PADDING,
  }),
  cellNested: css({
    '&[aria-selected=true]': {
      outline: 'none',
    },
  }),
  noDataNested: css({
    height: TABLE.NESTED_NO_DATA_HEIGHT,
    display: 'flex',
    alignItems: 'center',
    justifyContent: 'center',
    color: theme.colors.text.secondary,
    fontSize: theme.typography.h4.fontSize,
  }),
  cellActions: css({
    display: 'none',
    position: 'absolute',
    top: 0,
    margin: 'auto',
    height: '100%',
    color: theme.colors.text.primary,
    background: theme.isDark ? 'rgba(0, 0, 0, 0.7)' : 'rgba(255, 255, 255, 0.7)',
    padding: theme.spacing.x0_5,
    paddingInlineStart: theme.spacing.x1,
  }),
  cellActionsEnd: css({
    left: 0,
  }),
  cellActionsStart: css({
    right: 0,
  }),
  headerRow: css({
    paddingBlockStart: 0,
    fontWeight: 'normal',
    '& .rdg-cell': {
      height: '100%',
      alignItems: 'flex-end',
    },
  }),
  displayNone: css({
    display: 'none',
  }),
  paginationContainer: css({
    alignItems: 'center',
    display: 'flex',
    justifyContent: 'center',
    marginTop: '8px',
    width: '100%',
  }),
  paginationSummary: css({
    color: theme.colors.text.secondary,
    fontSize: theme.typography.bodySmall.fontSize,
    display: 'flex',
    justifyContent: 'flex-end',
    padding: theme.spacing(0, 1, 0, 2),
  }),
  menuItem: css({
    maxWidth: '200px',
  }),
});

const getFooterStyles = (justifyContent: Property.JustifyContent) => ({
  footerCellCountRows: css({
    display: 'flex',
    justifyContent: 'space-between',
  }),
  footerCell: css({
    display: 'flex',
    justifyContent: justifyContent || 'space-between',
  }),
});

const getHeaderCellStyles = (theme: GrafanaTheme2, justifyContent: Property.JustifyContent) => ({
  headerCell: css({
    display: 'flex',
    gap: theme.spacing(0.5),
    zIndex: theme.zIndex.tooltip - 1,
    paddingInline: TABLE.CELL_PADDING,
    paddingBlockEnd: TABLE.CELL_PADDING,
    justifyContent,
    '&:last-child': {
      borderInlineEnd: 'none',
    },
  }),
});

const getCellStyles = (
  theme: GrafanaTheme2,
  field: Field,
  rowHeight: number,
  shouldWrap: boolean,
  shouldOverflow: boolean,
  hasTooltip: boolean,
  colors: CellColors
) => {
  return {
    cell: css({
      textOverflow: 'initial',
      background: colors.bgColor ?? 'inherit',
      alignContent: 'center',
      justifyContent: getTextAlign(field),
      paddingInline: TABLE.CELL_PADDING,
      height: '100%',
      minHeight: rowHeight, // min height interacts with the fit-content property on the overflow container
      ...(shouldWrap && { whiteSpace: 'pre-line' }),
      ...(hasTooltip && { cursor: 'pointer' }),
      '&:last-child': {
        borderInlineEnd: 'none',
      },
      '&:hover': {
        background: colors.bgHoverColor,
        '.table-cell-actions': {
          display: 'flex',
        },
        ...(shouldOverflow && {
          zIndex: theme.zIndex.tooltip - 2,
          whiteSpace: 'pre-line',
          height: 'fit-content',
          minWidth: 'fit-content',
          paddingBlock: (rowHeight - TABLE.LINE_HEIGHT) / 2 - 1,
        }),
      },
    }),
  };
};
