/* eslint-disable jsx-a11y/no-static-element-interactions */
/* eslint-disable jsx-a11y/click-events-have-key-events */
import React, { useEffect, useState } from 'react';
import { ArrowRightIcon, ArrowLeftIcon, ChevronRightIcon, ChevronLeftIcon } from '@chakra-ui/icons';
import {
  Table,
  Tbody,
  Td,
  Th,
  Thead,
  Tooltip,
  Tr,
  Flex,
  IconButton,
  Text,
  Select,
  NumberInput,
  NumberInputField,
  NumberInputStepper,
  NumberIncrementStepper,
  NumberDecrementStepper,
  useColorModeValue,
  Box,
  Center,
  Spinner,
  Heading,
  useBreakpoint,
} from '@chakra-ui/react';
// @ts-ignore
import { useTranslation } from 'react-i18next';
import {
  useTable,
  usePagination,
  useSortBy,
  Row,
  UsePaginationInstanceProps,
  UseSortByInstanceProps,
  UsePaginationState,
  TableInstance,
} from 'react-table';
import { v4 as uuid } from 'uuid';
import SortIcon from './SortIcon';
import { isColumnSorted, isSortedDesc, onSortClick } from './utils';
import { LoadingOverlay } from 'components/LoadingOverlay';
import { Column, PageInfo, SortInfo } from 'models/Table';

interface Props {
  columns: Column<object>[];
  data: object[];
  count?: number;
  setPageInfo?: React.Dispatch<React.SetStateAction<PageInfo | undefined>>;
  sortInfo: SortInfo;
  setSortInfo: React.Dispatch<React.SetStateAction<SortInfo>>;
  isLoading?: boolean;
  obj: string;
  sortBy?: { id: string; desc: boolean }[];
  hiddenColumns?: string[];
  hideControls?: boolean;
  minHeight?: string;
  fullScreen?: boolean;
  isManual?: boolean;
  saveSettingsId?: string;
}

type TableInstanceWithHooks<T extends object> = TableInstance<T> &
  UsePaginationInstanceProps<T> &
  UseSortByInstanceProps<T> & {
    state: UsePaginationState<T>;
  };

const defaultProps = {
  count: undefined,
  setPageInfo: undefined,
  isLoading: false,
  minHeight: undefined,
  fullScreen: false,
  sortBy: [],
  hiddenColumns: [],
  hideControls: false,
  isManual: false,
  saveSettingsId: undefined,
};

const SortableDataTable: React.FC<Props> = ({
  columns,
  data,
  isLoading,
  obj,
  minHeight,
  fullScreen,
  sortBy,
  hiddenColumns,
  hideControls,
  count,
  sortInfo,
  setSortInfo,
  setPageInfo,
  isManual,
  saveSettingsId,
}) => {
  const { t } = useTranslation();
  const breakpoint = useBreakpoint();
  const hoveredRowBg = useColorModeValue('gray.100', 'gray.600');
  const textColor = useColorModeValue('gray.700', 'white');
  const getPageSize = () => {
    const saved = saveSettingsId ? localStorage.getItem(saveSettingsId) : undefined;
    if (saved) return Number.parseInt(saved, 10);
    return 10;
  };
  const [queryPageSize, setQueryPageSize] = useState(getPageSize());

  const {
    getTableProps,
    getTableBodyProps,
    headerGroups,
    prepareRow,
    page,
    canPreviousPage,
    canNextPage,
    pageOptions,
    pageCount,
    gotoPage,
    nextPage,
    previousPage,
    setPageSize,
    setHiddenColumns,
    state: { pageIndex, pageSize },
  } = useTable(
    {
      // @ts-ignore
      columns,
      data, // @ts-ignore
      initialState: { sortBy, pagination: !hideControls, pageSize: queryPageSize },
      manualPagination: isManual,
      pageCount: isManual && count !== undefined ? Math.ceil(count / queryPageSize) : undefined,
    },
    useSortBy,
    usePagination,
  ) as TableInstanceWithHooks<object>;

  useEffect(() => {
    if (setPageInfo && pageIndex !== undefined) setPageInfo({ index: pageIndex, limit: queryPageSize });
  }, [queryPageSize, pageIndex]);

  useEffect(() => {
    // @ts-ignore
    if (saveSettingsId) localStorage.setItem(saveSettingsId, pageSize);
    setQueryPageSize(pageSize);
  }, [pageSize]);

  useEffect(() => {
    if (hiddenColumns) setHiddenColumns(hiddenColumns);
  }, [hiddenColumns]);

  // If this is a manual DataTable, with a page index that is higher than 0 and higher than the max possible page, we send to index 0
  useEffect(() => {
    if (
      isManual &&
      setPageInfo &&
      data &&
      isManual &&
      pageIndex > 0 &&
      count !== undefined &&
      Math.ceil(count / queryPageSize) - 1 < pageIndex
    ) {
      gotoPage(0);
      setPageInfo({ index: 0, limit: queryPageSize });
    }
  }, [count, queryPageSize, page, data]);

  const computedMinHeight = () => {
    if (fullScreen) return { base: 'calc(100vh - 360px)', md: 'calc(100vh - 288px)' };
    return minHeight;
  };

  if (isLoading && data.length === 0) {
    return (
      <Center>
        <Spinner size="xl" />
      </Center>
    );
  }
  // Render the UI for your table
  return (
    <>
      <Box minHeight={computedMinHeight()} position="relative">
        <LoadingOverlay isLoading={isManual !== undefined && isManual && isLoading !== undefined && isLoading}>
          <Table {...getTableProps()} size="small" textColor={textColor} w="100%">
            <Thead fontSize="14px">
              {headerGroups.map((group) => (
                <Tr {...group.getHeaderGroupProps()}>
                  {group.headers.map((column) => (
                    <Th
                      color="gray.400"
                      {...column.getHeaderProps()}
                      // @ts-ignore
                      minWidth={column.customMinWidth ?? null}
                      // @ts-ignore
                      maxWidth={column.customMaxWidth ?? null}
                      // @ts-ignore
                      width={column.customWidth ?? null}
                    >
                      <div
                        onClick={() => onSortClick(column.id, sortInfo, setSortInfo)}
                        style={{ alignContent: 'center', overflow: 'hidden', whiteSpace: 'nowrap' }}
                      >
                        {column.render('Header')}
                        <SortIcon
                          // @ts-ignore
                          isSorted={isColumnSorted(column.id, sortInfo)}
                          // @ts-ignore
                          isSortedDesc={isSortedDesc(column.id, sortInfo)}
                          // @ts-ignore
                          canSort={column.canSort}
                        />
                      </div>
                    </Th>
                  ))}
                </Tr>
              ))}
            </Thead>
            {data.length > 0 && (
              <Tbody {...getTableBodyProps()}>
                {page.map((row: Row) => {
                  prepareRow(row);
                  return (
                    <Tr
                      {...row.getRowProps()}
                      key={uuid()}
                      _hover={{
                        backgroundColor: hoveredRowBg,
                      }}
                    >
                      {
                        // @ts-ignore
                        row.cells.map((cell) => (
                          <Td
                            key={uuid()}
                            px={1}
                            // @ts-ignore
                            minWidth={cell.column.customMinWidth ?? undefined}
                            // @ts-ignore
                            maxWidth={cell.column.customMaxWidth ?? undefined}
                            // @ts-ignore
                            width={cell.column.customWidth ?? undefined}
                            textOverflow="ellipsis"
                            overflow="hidden"
                            whiteSpace="nowrap"
                            fontSize="14px"
                            // @ts-ignore
                            textAlign={cell.column.isCentered ? 'center' : undefined}
                            fontFamily={
                              // @ts-ignore
                              cell.column.isMonospace
                                ? 'Inter, SFMono-Regular, Menlo, Monaco, Consolas, monospace'
                                : undefined
                            }
                          >
                            {cell.render('Cell')}
                          </Td>
                        ))
                      }
                    </Tr>
                  );
                })}
              </Tbody>
            )}
          </Table>
          {!isLoading && data.length === 0 && (
            <Center>
              <Heading pt={12}>{t('common.no_obj_found', { obj })}</Heading>
            </Center>
          )}
        </LoadingOverlay>
      </Box>
      {!hideControls && (
        <Flex justifyContent="space-between" m={4} alignItems="center">
          <Flex>
            <Tooltip label={t('table.first_page')}>
              <IconButton
                aria-label="Go to first page"
                onClick={() => gotoPage(0)}
                isDisabled={!canPreviousPage}
                icon={<ArrowLeftIcon h={3} w={3} />}
                mr={4}
              />
            </Tooltip>
            <Tooltip label={t('table.previous_page')}>
              <IconButton
                aria-label="Previous page"
                onClick={previousPage}
                isDisabled={!canPreviousPage}
                icon={<ChevronLeftIcon h={6} w={6} />}
              />
            </Tooltip>
          </Flex>

          <Flex alignItems="center">
            {breakpoint !== 'base' && (
              <>
                <Text flexShrink={0} mr={8}>
                  {t('table.page')}{' '}
                  <Text fontWeight="bold" as="span">
                    {pageIndex + 1}
                  </Text>{' '}
                  {t('common.of')}{' '}
                  <Text fontWeight="bold" as="span">
                    {pageOptions.length}
                  </Text>
                </Text>
                <Text flexShrink={0}>{t('table.go_to_page')}</Text>{' '}
                <NumberInput
                  ml={2}
                  mr={8}
                  w={28}
                  min={1}
                  max={pageOptions.length}
                  onChange={(_: unknown, numberValue: number) => {
                    const newPage = numberValue ? numberValue - 1 : 0;
                    gotoPage(newPage);
                  }}
                  defaultValue={pageIndex + 1}
                >
                  <NumberInputField />
                  <NumberInputStepper>
                    <NumberIncrementStepper />
                    <NumberDecrementStepper />
                  </NumberInputStepper>
                </NumberInput>
              </>
            )}
            <Select
              w={32}
              value={pageSize}
              onChange={(e: React.ChangeEvent<HTMLSelectElement>) => {
                setPageSize(Number(e.target.value));
              }}
            >
              {[10, 20, 30, 40, 50].map((opt) => (
                <option key={uuid()} value={opt}>
                  {t('common.show')} {opt}
                </option>
              ))}
            </Select>
          </Flex>

          <Flex>
            <Tooltip label={t('table.next_page')}>
              <IconButton
                aria-label="Go to next page"
                onClick={nextPage}
                isDisabled={!canNextPage}
                icon={<ChevronRightIcon h={6} w={6} />}
              />
            </Tooltip>
            <Tooltip label={t('table.last_page')}>
              <IconButton
                aria-label="Go to last page"
                onClick={() => gotoPage(pageCount - 1)}
                isDisabled={!canNextPage}
                icon={<ArrowRightIcon h={3} w={3} />}
                ml={4}
              />
            </Tooltip>
          </Flex>
        </Flex>
      )}
    </>
  );
};

SortableDataTable.defaultProps = defaultProps;

export default SortableDataTable;
