import debounce from 'lodash/debounce';
import { useCallback, useEffect, useRef } from 'react';

/**
 * Interfaces from Lodash
 * @see https://github.com/DefinitelyTyped/DefinitelyTyped/blob/ef8f9bb5ba32fb36ff978ac9898cc4f35cf7a9d6/types/lodash/common/function.d.ts#L405-L426
 */

/**
 * A hook which replaces imperative debounce from Lodash and works with React lifecycle
 * @param callback a function, **should** be wrapped in useCallback
 * @param delay in milliseconds
 * @param options check debounce options for description. Hook does not react to change of this object,
 * so it is not required to wrap it in useMemo or define outside component
 * @returns debounced function which has cancel and flush methods, always the same instance of a function.
 * @see https://lodash.com/docs/4.17.15#debounce
 */
export function useDebounce(callback, delay, options) {
  const savedCallback = useRef(debounce(callback, delay, options));
  useEffect(() => {
    savedCallback.current?.cancel();
    savedCallback.current = debounce(callback, delay, options);
    return () => savedCallback?.current?.cancel();
  }, [delay, callback]);
  const resultFunction = useCallback((...args) => savedCallback.current(...args), []);
  if (!resultFunction.cancel) {
    resultFunction.cancel = () => savedCallback.current?.cancel?.();
    resultFunction.flush = () => savedCallback.current?.flush?.();
  }
  return resultFunction;
}