// Understand why this code works perfectly here and not in material-hu. Using the library version, both the query and limit are not being correctly
// assigned the starting values - possibly due to a race condition between the form values creation and the default values assignment?
import { FC, useEffect, useCallback, useRef, useMemo } from 'react';
import {
  Control,
  Controller,
  FieldValues,
  UseFormReturn,
  useForm,
} from 'react-hook-form';

import { useDebounce } from '@material-hu/hooks/useDebounce';
import Close from '@material-hu/icons/material/Close';
import Search from '@material-hu/icons/material/Search';
import InputAdornment from '@material-hu/mui/InputAdornment';
import TableCell, { TableCellProps } from '@material-hu/mui/TableCell';
import TablePagination from '@material-hu/mui/TablePagination';
import TableSortLabel from '@material-hu/mui/TableSortLabel';
import TextField, { TextFieldProps } from '@material-hu/mui/TextField';
import { ArrowDropDownIcon } from '@material-hu/mui/x-date-pickers';

export type PaginationControllerProps<T extends FieldValues> = {
  control: Control<T>;
  total: number;
  setPage: (param: number) => void;
  setLimit: (param: number) => void;
  limitOptions: number[];
  labelRowsPerPage?: React.ReactNode;
};

const PaginationController: FC<
  PaginationControllerProps<FormValues>
> = props => {
  const {
    control,
    total,
    setPage,
    setLimit,
    limitOptions,
    labelRowsPerPage = '',
  } = props;

  return (
    <Controller
      control={control}
      name="pagination"
      render={({ field }) => (
        <TablePagination
          {...field}
          component="div"
          count={total}
          onPageChange={(_, page) => setPage(page)}
          onRowsPerPageChange={event =>
            setLimit(parseInt(event.target.value, 10))
          }
          labelRowsPerPage={labelRowsPerPage}
          page={field.value.page}
          rowsPerPage={field.value.limit}
          rowsPerPageOptions={limitOptions}
        />
      )}
    />
  );
};

type BuildSearchbarParams<T extends FieldValues> = {
  control: Control<T>;
  setValue: (name: keyof T, value: T[keyof T]) => void;
  defaultQuery?: string;
};

const buildSearchbar = ({
  control,
  setValue,
  defaultQuery = '',
}: BuildSearchbarParams<FormValues>) => {
  const SearchBarController: FC<TextFieldProps> = props => {
    return (
      <Controller
        control={control}
        name="query"
        defaultValue={defaultQuery}
        render={({ field }) => (
          <TextField
            fullWidth
            InputProps={{
              startAdornment: (
                <InputAdornment position="start">
                  <Search fontSize="small" />
                </InputAdornment>
              ),
              endAdornment: (
                <InputAdornment position="end">
                  <Close
                    fontSize="small"
                    sx={{
                      cursor: 'pointer',
                      visibility: field.value ? 'visible' : 'hidden',
                    }}
                    onClick={() => setValue('query', '')}
                  />
                </InputAdornment>
              ),
              inputProps: { maxLength: 255 },
            }}
            variant="outlined"
            {...field}
            {...props}
          />
        )}
      />
    );
  };
  return SearchBarController;
};

export default buildSearchbar;

type TableSortingHeaderProps = FC<
  TableCellProps & { id: string; disabled?: boolean }
>;

export type FormValues = {
  query: string;
  pagination: {
    page: number;
    limit: number;
  };
  order: string;
  orderBy: string;
};

type Form = UseFormReturn<FormValues, undefined>;

const useServerTableSorting = (form: Form) => {
  const { watch, setValue } = form;
  const { order, orderBy } = watch();

  const createSortHandler = (property: string) => () => {
    const isAsc = orderBy === property && order === 'ASC';
    setValue('orderBy', property);
    setValue('order', isAsc ? 'DESC' : 'ASC');
  };

  const TableSortingHeader: TableSortingHeaderProps = ({
    children,
    id,
    disabled,
    ...rest
  }) => (
    <TableCell
      {...rest}
      sortDirection={orderBy === id && order === 'ASC' ? 'asc' : 'desc'}
      sx={{ pr: 0 }}
    >
      <TableSortLabel
        active={orderBy === id}
        direction={orderBy === id && order === 'ASC' ? 'asc' : 'desc'}
        onClick={createSortHandler(id)}
        disabled={disabled}
        IconComponent={ArrowDropDownIcon}
      >
        {children}
      </TableSortLabel>
    </TableCell>
  );

  return TableSortingHeader;
};

type ServerPaginationOptions = {
  labelRowsPerPage?: string;
  defaultOrderBy?: string;
  defaultOrder?: string;
  defaultPage?: number;
  defaultLimit?: number;
  defaultQuery?: string;
  limitOptions?: number[];
};

const useTablePagination = (options?: ServerPaginationOptions) => {
  const {
    labelRowsPerPage = '',
    defaultOrderBy = 'CREATED_AT',
    defaultOrder = 'ASC',
    defaultPage = 0,
    defaultLimit = 10,
    defaultQuery = '',
    limitOptions = [10, 20, 30],
  } = options ?? {};

  const form = useForm<FormValues>({
    defaultValues: {
      query: defaultQuery,
      pagination: {
        page: defaultPage,
        limit:
          defaultLimit === limitOptions[0] ? limitOptions[0] : defaultLimit,
      },
      order: defaultOrder,
      orderBy: defaultOrderBy,
    },
  });

  const { watch, setValue, control } = form;
  const { query, pagination, order, orderBy } = watch();

  const setPage = useCallback(
    (page: number) => setValue('pagination.page', page),
    [setValue],
  );
  const setLimit = useCallback(
    (limit: number) => setValue('pagination.limit', limit),
    [setValue],
  );
  const setOrderBy = useCallback(
    (newOrderBy: string) => setValue('orderBy', newOrderBy),
    [setValue],
  );
  const setOrder = useCallback(
    (newOrder?: string) =>
      setValue('order', newOrder || order === 'DESC' ? 'ASC' : 'DESC'),
    [setValue],
  );
  const setQuery = useCallback(
    (newQuery: string) => setValue('query', newQuery),
    [setValue],
  );

  const TableSortingHeader = useServerTableSorting(form);

  const paginationController = (total: number) => (
    <PaginationController
      control={control}
      total={total}
      setPage={setPage}
      setLimit={setLimit}
      limitOptions={limitOptions}
      labelRowsPerPage={labelRowsPerPage}
    />
  );

  const debouncedQuery = useDebounce(query);
  const prevDebouncedQueryRef = useRef(debouncedQuery);
  const prevLimitRef = useRef(pagination.limit);

  useEffect(() => {
    const debouncedQueryChanged =
      prevDebouncedQueryRef.current !== debouncedQuery;
    const limitChanged = prevLimitRef.current !== pagination.limit;

    // Update refs with current values for next comparison
    prevDebouncedQueryRef.current = debouncedQuery;
    prevLimitRef.current = pagination.limit;

    // Only reset page if debouncedQuery or limit actually changed
    if (debouncedQueryChanged || limitChanged) {
      setPage(0);
    }
  }, [debouncedQuery, pagination.limit]);

  const Searchbar = useMemo(
    () => buildSearchbar({ control, setValue, defaultQuery }),
    [],
  );

  return {
    query: debouncedQuery,
    pagination,
    Searchbar,
    paginationController,
    orderBy,
    order,
    TableSortingHeader,
    setQuery,
    setPage,
    setOrderBy,
    setOrder,
  };
};

export { useTablePagination };
export type { TableSortingHeaderProps };
