import React, { useEffect, useState, useMemo, useRef } from 'react';
import classnames from 'classnames';
import ResizeObserver from 'resize-observer-polyfill';
import { getCoreRowModel, useReactTable } from '@tanstack/react-table';
import uniqueId from 'lodash-es/uniqueId';
import { TableHeader } from './table-header';
import { TableBody } from './table-body';
import { TableHeaderCell } from './table-header-cell';
import { TableBodyCell } from './table-body-cell';
import { TableToolbar } from './table-toolbar';
import { EmptyStateWrapper } from './empty-state-wrapper';
import { useColumns } from '../hooks/useColumns';
import { useVirtual } from '../hooks/useVirtual';
import { useScroll } from '../hooks/useScroll';
import { useLoading } from '../hooks/useLoading';
import { useDebounce } from '../../hooks/use-debounce';
import { TableKeyboardNavigationContext, useTableKeyboardNavigation } from '../../hooks/use-table-keyboard-navigation';
import { useInnerStateFallback } from '../hooks/useInnerStateFallback';
import { decoratePinnedColumns } from '../utils/decoreatePinnedColumns';
import { PaginationNew } from '../../pagination-new';
import { COLUMN_SIZE, TABLE_SIZE } from '../types';
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
export function Table({
  columns,
  data,
  getFilterBody,
  getToolBarActions,
  getToolbarRightActions,
  getEmptyState = () => /*#__PURE__*/_jsx(EmptyStateWrapper, {}),
  TableBodyComponent = TableBody,
  TableHeaderComponent = TableHeader,
  ThComponent = TableHeaderCell,
  TdComponent = TableBodyCell,
  trClassName,
  thClassName,
  tdClassName,
  expandedContentClassName,
  tableMeta,
  onCellClick,
  onColumnHeaderClick,
  selected,
  sorting,
  onSortingChange,
  pinned,
  expandedRows,
  ExpandedRowComponent,
  columnOrder,
  onColumnOrderChange,
  pagination,
  onPaginationChange,
  totalCount,
  totalLimit,
  isExceededTotalLimit = !!totalLimit && (!totalCount || totalCount >= totalLimit),
  nextPage,
  width,
  errorMessage,
  loadingRowCount,
  getRowId,
  visible,
  onRowClick,
  isRowClickDisabled,
  isRowSelectionDisabled = () => false,
  pageSizeOptions,
  enableRowSelection = false,
  enableCheckBoxes = false,
  enableMultiRowSelection = true,
  isLoading = false,
  defaultColumnSizing = {},
  touched,
  onTouchedChange = () => {},
  onColumnSizingChange = () => {},
  onPinningChange = () => {},
  onExpandedChange = () => {},
  onRowSelectionChange = () => {},
  getRowActions,
  getRowFooterAction,
  getCustomRowContent,
  localizationProps = {},
  defaultState = {},
  onVisibilityChange = () => {},
  enableToolbar = false,
  enableSettings = false,
  bottomEndScrollOffset = 200,
  onScroll = () => {},
  onScrollEnd = () => {},
  afterLoading = false,
  enablePagination = false,
  enableSorting = false,
  enableSortingRemoval = true,
  enableColumnResizing = true,
  tableTestId = 'react-table',
  tableRowTestId = 'react-table-tr',
  tableHeaderRowTestId = 'react-table-tr',
  tableHeaderCellTestId = 'react-table-th',
  tableBodyCellTestId = 'react-table-td',
  size = TABLE_SIZE.MEDIUM,
  enablePinning = true
}) {
  const [columnSizing, setColumnSizing] = useState(defaultColumnSizing);
  const [centerScrolled, setCenterScrolled] = useState(false);
  const [scrollableRight, setScrollableRight] = useState(false);
  const [tableId] = useState(() => uniqueId());
  const [visibility, setVisibility] = useState(visible || {});
  const tableRef = useRef(null);
  const [isVerticalScrollPresent, setIsVerticalScrollPresent] = useState(tableRef?.current?.scrollHeight !== tableRef?.current?.clientHeight);

  // we need change width depending on whether we have the scroll or not
  useEffect(() => {
    const checkScroll = () => {
      setIsVerticalScrollPresent(tableRef?.current?.scrollHeight !== tableRef?.current?.clientHeight);
    };
    if (tableRef?.current) {
      const observer = new ResizeObserver(() => window.requestAnimationFrame(checkScroll));
      observer.observe(tableRef?.current);
      return () => observer.disconnect();
    }
  }, []);
  const debouncedColumnSizingChange = useDebounce(onColumnSizingChange, 600);
  const [columnPinning, onColumnPinningChange] = useInnerStateFallback(onPinningChange, pinned, decoratePinnedColumns);
  const [rowSelection, setRowSelection] = useInnerStateFallback(onRowSelectionChange, selected);
  const [expanded, setExpanded] = useInnerStateFallback(onExpandedChange, expandedRows);
  const isRowExpandEnabled = !!ExpandedRowComponent;
  const isPaginationLoading = !totalCount && isLoading && !errorMessage;
  const pageCount = useMemo(() => {
    if (isExceededTotalLimit) {
      return undefined;
    }
    return Math.ceil((totalCount ?? 0) / (pagination?.pageSize ?? 1));
  }, [pagination?.pageSize, totalCount, totalLimit, isExceededTotalLimit]);
  useEffect(() => {
    debouncedColumnSizingChange(columnSizing);
  }, [columnSizing]);
  useEffect(() => onVisibilityChange(visibility), [visibility]);
  useEffect(() => {
    // only row actions can be pinned right, so no need to trigger state updates if table doesn't have row actions
    if (getRowActions) {
      const hasReachedEnd = (tableRef?.current?.clientWidth ?? 0) + (tableRef?.current?.scrollLeft ?? 0) >= (tableRef?.current?.scrollWidth ?? 0);
      setScrollableRight(!hasReachedEnd);
    }
    // Component width depends on Vertical scroll
  }, [columnSizing, width, isVerticalScrollPresent]);
  const decoratedColumns = useColumns(columns, enableCheckBoxes, width - (isVerticalScrollPresent ? COLUMN_SIZE.SCROLL_OFFSET : 0), visibility, columnSizing, isRowExpandEnabled, !!getRowActions, localizationProps?.tableBody);
  const onChangeSize = fn => {
    const nextData = typeof fn === 'function' ? fn(columnSizing) : fn;
    const targetData = typeof fn === 'function' ? fn({}) : fn;
    const isEmpty = !Object.keys(nextData).length;
    if (isEmpty) {
      onTouchedChange();
      return setColumnSizing({});
    }
    const baseData = decoratedColumns.reduce((acc, current) => ({
      ...acc,
      [current.accessorKey]: current.size
    }), {});
    onTouchedChange({
      width,
      columns: [...(touched?.columns || []), ...Object.keys(targetData)]
    });
    setColumnSizing({
      ...baseData,
      ...nextData
    });
  };
  useEffect(() => {
    // if container width changed we should resize table for full width but save touched resized columns
    if (touched && touched.width !== width) {
      const touchedResizedColumns = touched.columns.reduce((acc, current) => {
        if (columnSizing[current]) {
          return {
            ...acc,
            [current]: columnSizing[current]
          };
        }
        return acc;
      }, {});
      onTouchedChange({
        ...touched,
        width
      });
      setColumnSizing(touchedResizedColumns);
    }
  }, [width]);
  const tableData = useLoading(isLoading, afterLoading, data, loadingRowCount || pagination?.pageSize);
  const isEmpty = tableData.length === 0 && !errorMessage;
  const tableModel = useReactTable({
    data: tableData,
    columns: decoratedColumns,
    getCoreRowModel: getCoreRowModel(),
    state: {
      rowSelection,
      columnPinning,
      columnSizing,
      sorting,
      pagination,
      columnVisibility: visible || visibility,
      expanded,
      columnOrder
    },
    pageCount,
    onColumnVisibilityChange: setVisibility,
    onColumnOrderChange,
    manualPagination: true,
    onPaginationChange,
    columnResizeMode: 'onChange',
    onColumnSizingChange: onChangeSize,
    onSortingChange,
    onColumnPinningChange,
    onRowSelectionChange: enableRowSelection || enableCheckBoxes ? setRowSelection : undefined,
    enableRowSelection: enableRowSelection || enableCheckBoxes ? row => !isRowSelectionDisabled(row.original) : false,
    enableMultiRowSelection,
    enableSorting,
    enableSortingRemoval,
    sortDescFirst: true,
    manualSorting: true,
    enablePinning,
    enableColumnResizing,
    enableExpanding: isRowExpandEnabled,
    onExpandedChange: isRowExpandEnabled ? setExpanded : undefined,
    getRowCanExpand: () => true,
    // TODO add an ability to define it from outside @vovkvlad
    manualExpanding: true,
    getRowId,
    meta: {
      ...tableMeta,
      tableId,
      isLoading
    }
  });
  const {
    paddingTop,
    paddingBottom,
    virtualRows,
    measureElement
  } = useVirtual(tableRef, tableModel, size);
  const keyboardNavigationController = useTableKeyboardNavigation({
    tableRef,
    columnsCount: tableModel.getVisibleFlatColumns().length,
    // for both infinite scroll and paginated tables, we would rely on how much is available atm
    // (on one page for paginated version and how much loaded so fat for infinite scroll)
    rowsCount: tableData.length
  });
  useScroll(tableRef, bottomEndScrollOffset, onScrollEnd, afterLoading);
  return /*#__PURE__*/_jsxs("div", {
    className: "table-components-container",
    children: [(enableToolbar || enableSettings) && /*#__PURE__*/_jsx(TableToolbar, {
      getToolBarActions: getToolBarActions,
      getToolbarRightActions: getToolbarRightActions,
      enableSorting: enableSorting,
      enablePinning: enablePinning,
      tableModel: tableModel,
      defaultState: defaultState,
      baseColumns: decoratedColumns,
      hideSettings: !enableSettings,
      tableContainerWidth: width,
      localizationText: localizationProps?.headerToolbar
    }), getFilterBody && getFilterBody(), /*#__PURE__*/_jsxs("div", {
      className: classnames('react-table-container', size === TABLE_SIZE.NESTED && 'nested'),
      children: [/*#__PURE__*/_jsxs("div", {
        className: "table-container",
        ref: tableRef,
        onScroll: e => {
          // only row actions can be pinned right, so no need to trigger state updates if table doesn't have row actions
          if (getRowActions) {
            const hasReachedEnd = e.target.clientWidth + e.target.scrollLeft >= e.target.scrollWidth;
            setScrollableRight(!hasReachedEnd);
          }
          setCenterScrolled(!!e.target.scrollLeft);
          onScroll(e);
        },
        children: [/*#__PURE__*/_jsx(TableKeyboardNavigationContext.Provider, {
          value: keyboardNavigationController,
          children: /*#__PURE__*/_jsxs("table", {
            className: classnames('react-table', isLoading && 'react-table-loading'),
            "test-id": tableTestId
            // eslint-disable-next-line react/forbid-dom-props
            ,
            style: {
              width: tableModel.getTotalSize()
            },
            role: tableModel.options.enableExpanding ? 'treegrid' : 'grid',
            onKeyDown: keyboardNavigationController.onTableKeyDownHandler,
            "aria-label": localizationProps?.tableAriaLabel,
            children: [/*#__PURE__*/_jsx(TableHeaderComponent, {
              size: size,
              tableModel: tableModel,
              ThElement: ThComponent,
              trClassName: typeof trClassName !== 'function' ? trClassName : '',
              thClassName: thClassName,
              centerScrolled: centerScrolled,
              scrollableRight: scrollableRight,
              onColumnHeaderClick: onColumnHeaderClick,
              tableContainerWidth: width,
              localizationText: localizationProps?.tableHeader,
              tableRowTestId: tableHeaderRowTestId,
              tableHeaderCellTestId: tableHeaderCellTestId
            }), !isEmpty && !errorMessage ? /*#__PURE__*/_jsx(TableBodyComponent, {
              trClassName: trClassName,
              tableRowTestId: tableRowTestId,
              size: size,
              tableModel: tableModel,
              TdElement: TdComponent,
              tdClassName: tdClassName,
              selectable: enableRowSelection,
              centerScrolled: centerScrolled,
              scrollableRight: scrollableRight,
              onCellClick: onCellClick,
              paddingTop: paddingTop,
              onRowClick: onRowClick,
              isRowClickDisabled: isRowClickDisabled,
              isRowSelectionDisabled: isRowSelectionDisabled,
              paddingBottom: paddingBottom,
              virtualRows: virtualRows,
              ExpandedRowComponent: ExpandedRowComponent,
              measureElement: measureElement,
              getCustomRowContent: getCustomRowContent,
              getRowActions: getRowActions,
              getRowFooterAction: getRowFooterAction,
              expandedContentClassName: expandedContentClassName,
              tableContainerWidth: width - (isVerticalScrollPresent ? COLUMN_SIZE.SCROLL_OFFSET : 0),
              localizationProps: localizationProps?.tableBody,
              tableBodyCellTestId: tableBodyCellTestId
            }) : null]
          })
        }), isEmpty ? /*#__PURE__*/_jsx("div", {
          className: "table-empty-state",
          children: getEmptyState()
        }) : null, errorMessage ? /*#__PURE__*/_jsx("div", {
          className: "table-error-message",
          "test-id": "table-error-message",
          children: /*#__PURE__*/_jsx("p", {
            className: "error-message",
            children: errorMessage
          })
        }) : null]
      }), enablePagination ? /*#__PURE__*/_jsx("div", {
        className: "table-footer",
        "test-id": "table-pagination-container",
        children: /*#__PURE__*/_jsx(PaginationNew, {
          isLoading: isPaginationLoading,
          selectedPage: tableModel.getState().pagination.pageIndex,
          total: totalCount || 0,
          totalLimit: totalLimit,
          isExceededTotalLimit: isExceededTotalLimit,
          nextPage: nextPage,
          itemsPerPage: tableModel.getState().pagination.pageSize,
          onItemsPerPageChange: tableModel.setPageSize,
          onPageSelect: tableModel.setPageIndex,
          pageSizeOptions: pageSizeOptions,
          ...localizationProps?.pagination
        })
      }) : null]
    })]
  });
}