import { ExpandMore } from '@mui/icons-material'
import {
  Box,
  Checkbox,
  IconButton,
  ListItemIcon,
  MenuItem,
  Stack,
  Table,
  TableCell,
  TableHead,
  TableRow,
  TextField,
  Typography,
} from '@mui/material'
import { useCallback, useEffect, useMemo, useState } from 'react'
import { usePagination, useRowSelect, useTable } from 'react-table'
import { debounce } from '../../../utils/debounce'
import TableStats from '../common/TableStats'
import { SortAscIcon, SortDescIcon, SortIcon } from '../common/icons'
import {
  StyledLimitBox,
  StyledLimitMenu,
  StyledPagination,
  StyledTableFooter,
} from '../common/styles'
import { ColumnProps, Sort, TableProps } from '../common/types'
import BatchActionsHeader from './BatchActionsHeader'
import { RenderTableBody } from './RenderTableBody'

type ReactTableCell = {
  Header: any
  accessor: any
  Cell?: any
  sort?: boolean
} & ColumnProps

const selectColumn = {
  id: 'selection',
  Header: ({ getToggleAllPageRowsSelectedProps }) => (
    <Checkbox
      onClick={(e) => {
        e.stopPropagation()
      }}
      {...getToggleAllPageRowsSelectedProps()}
    />
  ),
  Cell: ({ row }) => (
    <Checkbox
      onClick={(e) => {
        e.stopPropagation()
      }}
      {...row.getToggleRowSelectedProps()}
    />
  ),
}

const getTableCol = (headerItem: ColumnProps) => {
  let col: ReactTableCell = {
    Header: headerItem?.title,
    accessor: headerItem?.dataIndex,
    ...headerItem,
  }

  if (headerItem?.render) {
    return {
      ...col,
      Cell: ({ row }) =>
        headerItem?.render(
          row?.original[headerItem?.dataIndex],
          row?.original,
          row?.index,
        ),
    }
  }
  return col
}

export default function ReactTable({
  showSerialNumber = false,
  columns,
  rowKey = 'id',
  dataSource,
  loading,
  pagination,
  refetching,
  onSort,
  select = {
    enable: false,
  },
  onRowClick,
}: TableProps) {
  const [sort, setSort] = useState<Sort>({})

  const headers = useMemo(() => {
    if (!columns) return []

    return columns?.map((item) => getTableCol(item))
  }, [columns])

  const tableData = useMemo(() => {
    if (!dataSource) return []
    return [...dataSource]
  }, [dataSource])

  const {
    getTableProps,
    getTableBodyProps,
    headerGroups,
    rows,
    prepareRow,
    pageCount,
    gotoPage,
    setPageSize,
    toggleAllRowsSelected,
    state: { pageIndex, pageSize, selectedRowIds },
  } = useTable(
    {
      columns: headers || [],
      data: tableData || [],
      stateReducer: (newState, action) => {
        switch (action.type) {
          case 'toggleAllRowsSelected':
            return { ...newState, selectedRowIds: {} }
          default:
            return newState
        }
      },
      initialState: {
        pageIndex: pagination?.page ?? 0,
        pageSize: pagination?.limit ?? 10,
      },
      manualPagination: true,
      autoResetSelectedRows: false,
      pageCount: pagination?.totalCount ?? dataSource?.length,
      getRowId: select?.enable ? (row) => row[rowKey] : undefined,
    },
    // useSortBy,
    usePagination,
    useRowSelect,
    (hooks) => {
      if (!select.enable) return
      hooks.visibleColumns.push((columns) => [selectColumn, ...columns])
    },
  )
  const [pageInput, setPageInput] = useState<string>(String(pageIndex + 1))

  const debouncedHandlePagination = useCallback(
    debounce((e, value) => handlePagination(e, value), 500),
    [],
  )

  const handleSortClick = (sortBykey) => {
    setSort((prev) => {
      if (prev[sortBykey]) {
        if (prev[sortBykey] === 'desc') return { ...prev, [sortBykey]: 'asc' }
        if (prev[sortBykey] === 'asc') {
          delete prev[sortBykey]
          return { ...prev }
        }
      } else {
        return {
          ...prev,
          [sortBykey]: 'desc',
        }
      }
    })
  }
  useEffect(() => {
    if (!onSort) return

    onSort({
      sortBy: Object.keys(sort).join(','),
      sortOrder: Object.keys(sort)
        .map((item) => sort[item])
        .join(','),
    })
  }, [sort])

  const handlePagination = (e, value) => {
    gotoPage(value - 1)
  }

  useEffect(() => {
    if (!pagination) return
    pagination.onChange(pageIndex * pageSize)
  }, [pageIndex])

  useEffect(() => {
    if (!pagination) return
    pagination.onChangeLimit(pageSize)
    pagination.onChange(0)
  }, [pageSize])

  const clearSelection = () => {
    toggleAllRowsSelected(false)
  }

  return (
    <>
      <BatchActionsHeader
        actions={select?.actions || []}
        selectedRowIds={selectedRowIds}
        clearSelection={clearSelection}
      />
      <Table sx={{ position: 'relative' }} {...getTableProps()}>
        <TableHead>
          {headerGroups.map((headerGroup, index) => (
            <TableRow key={index} {...headerGroup.getHeaderGroupProps()}>
              {showSerialNumber && <TableCell>S. No.</TableCell>}
              {headerGroup.headers.map((column: any, index) => (
                <TableCell key={index} {...column.getHeaderProps()}>
                  {column.render('Header')}
                  {column.sort && (
                    <IconButton onClick={() => handleSortClick(column.id)}>
                      <ListItemIcon>
                        {sort[column.id] === 'asc' ? (
                          <SortAscIcon />
                        ) : sort[column.id] === 'desc' ? (
                          <SortDescIcon />
                        ) : (
                          <SortIcon />
                        )}
                      </ListItemIcon>
                    </IconButton>
                  )}
                </TableCell>
              ))}
            </TableRow>
          ))}
        </TableHead>
        <RenderTableBody
          showSerialNumber={showSerialNumber}
          onRowClick={onRowClick}
          colLength={columns?.length}
          loading={loading || refetching}
          getTableBodyProps={getTableBodyProps}
          rows={rows}
          prepareRow={prepareRow}
        />
      </Table>
      {pagination && dataSource && !loading && (
        <StyledTableFooter>
          <TableStats
            limit={pageSize}
            page={pageIndex + 1}
            totalCount={pageCount}
          />
          <Stack direction={'row'} gap={2} alignItems={'center'}>
            <StyledPagination
              variant="outlined"
              shape="rounded"
              onChange={handlePagination}
              count={Math.ceil(pageCount / pageSize)}
              page={pageIndex + 1}
            />
            {pagination.handlePageInput && (
              <Box
                sx={{
                  '& .MuiBox-root': {
                    display: 'flex',
                    alignItems: 'center',
                    gap: '5px',
                  },
                }}
              >
                <TextField
                  variant="outlined"
                  label={'Page :'}
                  size="small"
                  value={pageInput}
                  onChange={(e) => {
                    setPageInput(e.target.value)
                    debouncedHandlePagination(e, Number(e.target.value))
                  }}
                  sx={{ width: '80px' }}
                />
              </Box>
            )}
          </Stack>
          <Limit pageSize={pageSize} setPageSize={setPageSize} />
        </StyledTableFooter>
      )}
    </>
  )
}

const Limit = ({ pageSize, setPageSize }) => {
  const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null)
  const open = Boolean(anchorEl)

  const handleClick = (event) => {
    setAnchorEl(event.currentTarget)
  }

  const handleClose = () => {
    setAnchorEl(null)
  }
  const menuItems = [
    { label: '10', value: 10 },
    { label: '20', value: 20 },
    { label: '50', value: 50 },
    { label: '100', value: 100 },
  ]

  return (
    <StyledLimitBox open={open}>
      <Box onClick={handleClick}>
        <Typography>{`${pageSize} / Page`}</Typography>
        <ExpandMore />
      </Box>
      <StyledLimitMenu
        id="basic-menu"
        anchorEl={anchorEl}
        open={open}
        onClose={handleClose}
      >
        {menuItems?.map((item, index) => (
          <MenuItem
            onClick={() => {
              setPageSize(item?.value)
              handleClose()
            }}
            value={item?.value}
            key={index}
          >
            {item?.label}
          </MenuItem>
        ))}
      </StyledLimitMenu>
    </StyledLimitBox>
  )
}
