import { memo, useCallback, useMemo } from 'react';
import type { MouseEvent as ReactMouseEvent } from 'react';
import clsx from 'clsx';

import Grid from '@mui/material/Grid';
import TablePagination from '@mui/material/TablePagination';
import type { TablePaginationProps } from '@mui/material/TablePagination';
import type { TablePaginationActionsProps } from '@mui/material/TablePagination/TablePaginationActions';
import usePagination from '@mui/material/usePagination';
import ChevronLeftRoundedIcon from '@mui/icons-material/ChevronLeftRounded';
import ChevronRightRoundedIcon from '@mui/icons-material/ChevronRightRounded';

import createClasses from './styles';

const PaginationComponent = memo((props: TablePaginationActionsProps) => {
  const {
    backIconButtonProps,
    count,
    nextIconButtonProps,
    page,
    rowsPerPage,
    getItemAriaLabel,
    onPageChange,
    className
  } = props;

  const styles = createClasses();

  const { items } = usePagination({
    count: Math.ceil(count / rowsPerPage),
    // eslint-disable-next-line
    onChange: (e: any, pageNumber) => onPageChange(e, pageNumber - 1), // Mui types dont corelate
    page: page + 1
  });

  const itemsIds = useMemo(() => items.map(() => Math.random()), [items]);
  const handleEllipsisClick = useCallback(
    state => (e: ReactMouseEvent<HTMLButtonElement, MouseEvent>) =>
      onPageChange(e, page + (state === 'next' ? 4 : -4)),
    [onPageChange, page]
  );

  return (
    <nav className={className}>
      <ul className={styles.ul}>
        {items.map(({ page: itemPage, type, selected, ...item }, index) => {
          let children = null;

          switch (type) {
            case 'start-ellipsis':
            case 'end-ellipsis':
              children = (
                <button
                  type="button"
                  className={clsx(
                    styles.paginationButton,
                    styles.pageEllipsisButton,
                    styles.marginLeft2,
                    styles.marginRight2
                  )}
                  onClick={handleEllipsisClick(type === 'end-ellipsis' ? 'next' : 'previous')}
                  data-testid={type}
                >
                  …
                </button>
              );
              break;

            case 'page':
              children = (
                <button
                  aria-label={`Page ${itemPage}`}
                  type="button"
                  className={clsx(
                    styles.paginationButton,
                    styles.pageNumberButton,
                    styles.marginLeft2,
                    styles.marginRight2,
                    selected && styles.paginationButtonSelected,
                    itemPage && itemPage.toString().length > 2 && styles.largeButton
                  )}
                  {...item}
                >
                  {itemPage}
                </button>
              );
              break;

            case 'previous':
              children = (
                <button
                  aria-label={getItemAriaLabel('previous')}
                  data-testid="previousButton"
                  type="button"
                  className={`${styles.paginationButton} ${styles.marginRight2}`}
                  {...item}
                  {...backIconButtonProps}
                >
                  <ChevronLeftRoundedIcon />
                </button>
              );
              break;

            case 'next':
              children = (
                <button
                  aria-label={getItemAriaLabel('next')}
                  data-testid="nextButton"
                  data-cy="nextButton"
                  type="button"
                  className={`${styles.paginationButton} ${styles.marginLeft2}`}
                  {...item}
                  {...nextIconButtonProps}
                >
                  <ChevronRightRoundedIcon />
                </button>
              );
              break;
          }

          return (
            <Grid item component="li" key={itemsIds[index]}>
              {children}
            </Grid>
          );
        })}
      </ul>
    </nav>
  );
});

export interface PaginationProps
  extends Omit<
    TablePaginationProps,
    | 'ActionsComponent'
    | 'onPageChange'
    | 'onRowsPerPageChange'
    | 'rowsPerPageOptions'
    | 'SelectProps'
    | 'showFirstButton'
    | 'showLastButton'
  > {
  /**
   * Current page, starts count from 0.
   */
  page: number;
  /**
   * Callback on change page.
   */
  onChangePage: TablePaginationProps['onPageChange'];
  /**
   * Total number of items.
   */
  count: number;
  /**
   * Number of items to be displayed in a page.
   */
  rowsPerPage: number;
  /**
   * Hide pagination action buttons?
   */
  hideNavigation?: boolean;
  /**
   * Hide displayed rows text?
   */
  hideRowCount?: boolean;
  classes?: TablePaginationProps['classes'] & Partial<ReturnType<typeof createClasses>>;
}

const Pagination = (props: PaginationProps) => {
  const {
    page,
    onChangePage,
    count,
    rowsPerPage,
    hideNavigation,
    hideRowCount,
    classes,
    ...otherProps
  } = props;
  const styles = createClasses();

  const labelDisplayedRows: TablePaginationProps['labelDisplayedRows'] = useCallback(
    ({ from, to, count: curCount }) => `${from}-${to} of ${curCount}`,
    []
  );

  return (
    <TablePagination
      component="div"
      rowsPerPageOptions={[0]}
      ActionsComponent={PaginationComponent}
      count={count}
      rowsPerPage={rowsPerPage}
      page={page}
      labelDisplayedRows={labelDisplayedRows}
      labelRowsPerPage="Rows per page"
      {...otherProps}
      onPageChange={onChangePage}
      classes={{
        ...classes,
        root: clsx(styles.root, classes?.root),
        actions: clsx(classes?.actions, hideNavigation && styles.displayNone),
        displayedRows: clsx(classes?.displayedRows, hideRowCount && styles.displayNone),
        spacer: clsx(classes?.spacer, styles.displayNone, classes?.displayNone),
        toolbar: clsx(styles.toolbar, classes?.toolbar)
      }}
    />
  );
};

const m = memo(Pagination);
export { m as Pagination };
