import { useCallback, useMemo, useRef } from 'react';

import FormPagination from '@material-hu/components/design-system/Inputs/Pagination/form';
import FormSearch from '@material-hu/components/design-system/Inputs/Search/form';

export const LIMIT_OPTIONS = [10, 20, 30];

export type SearchControllerProps = {
  onChangeSearch: (newSearch: string) => void;
  inputProps?: Record<string, unknown>;
};

const SearchController = ({
  onChangeSearch,
  inputProps,
}: SearchControllerProps) => (
  <FormSearch
    name="params.search"
    inputProps={{
      ...inputProps,
      onChange: onChangeSearch,
    }}
  />
);

export type PaginationControllerProps = {
  onChangePage: (newPage: number) => void;
  onChangeLimit: (newLimit: number) => void;
  inputProps?: Record<string, unknown>;
};

const PaginationController = ({
  onChangePage,
  onChangeLimit,
  inputProps,
}: PaginationControllerProps) => (
  <FormPagination
    name="params.pagination"
    inputProps={{
      ...inputProps,
      limitOptions: LIMIT_OPTIONS,
      onChangeLimit,
      onChangePage,
    }}
  />
);

type Handlers = {
  onChangeSearch: (newSearch: string) => void;
  onChangePage: (newPage: number) => void;
  onChangeLimit: (newLimit: number) => void;
};

// Builds referentially-stable Search and Pagination controller wrappers that
// always invoke the latest handler closures. Refs absorb handler identity
// changes (e.g. setUrlState updates) so the wrapper components keep the same
// component identity across renders, avoiding the unmount/remount cycle that
// defining components inline inside useMemo would cause.
const useSearchPaginationControllers = ({
  onChangeSearch,
  onChangePage,
  onChangeLimit,
}: Handlers) => {
  const onChangeSearchRef = useRef(onChangeSearch);
  const onChangePageRef = useRef(onChangePage);
  const onChangeLimitRef = useRef(onChangeLimit);
  onChangeSearchRef.current = onChangeSearch;
  onChangePageRef.current = onChangePage;
  onChangeLimitRef.current = onChangeLimit;

  const stableOnChangeSearch = useCallback(
    (newSearch: string) => onChangeSearchRef.current(newSearch),
    [],
  );
  const stableOnChangePage = useCallback(
    (newPage: number) => onChangePageRef.current(newPage),
    [],
  );
  const stableOnChangeLimit = useCallback(
    (newLimit: number) => onChangeLimitRef.current(newLimit),
    [],
  );

  const Search = useMemo(
    () =>
      (props: Omit<SearchControllerProps, 'onChangeSearch'> = {}) => (
        <SearchController
          {...props}
          onChangeSearch={stableOnChangeSearch}
        />
      ),
    [stableOnChangeSearch],
  );

  const Pagination = useMemo(
    () =>
      (
        props: Omit<
          PaginationControllerProps,
          'onChangePage' | 'onChangeLimit'
        > = {},
      ) => (
        <PaginationController
          {...props}
          onChangePage={stableOnChangePage}
          onChangeLimit={stableOnChangeLimit}
        />
      ),
    [stableOnChangePage, stableOnChangeLimit],
  );

  return { Search, Pagination };
};

export default useSearchPaginationControllers;
