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

var useDebouncedCallback = function useDebouncedCallback(callback, delay) {
  var _ref = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {},
      maxWait = _ref.maxWait,
      leading = _ref.leading,
      optionsTrailing = _ref.trailing;

  var maxWaitHandler = useRef(null);
  var maxWaitArgs = useRef([]);
  var trailing = optionsTrailing === undefined ? true : optionsTrailing;
  var leadingCall = useRef(false);
  var functionTimeoutHandler = useRef(null);
  var isComponentUnmounted = useRef(false);
  var debouncedFunction = useRef(callback);
  debouncedFunction.current = callback;
  var cancelDebouncedCallback = useCallback(function () {
    clearTimeout(functionTimeoutHandler.current);
    clearTimeout(maxWaitHandler.current);
    maxWaitHandler.current = null;
    maxWaitArgs.current = [];
    functionTimeoutHandler.current = null;
    leadingCall.current = false;
  }, []);
  useEffect(function () {
    return function () {
      // we use flag, as we allow to call callPending outside the hook
      isComponentUnmounted.current = true;
    };
  }, []);
  var debouncedCallback = useCallback(function () {
    for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
      args[_key] = arguments[_key];
    }

    maxWaitArgs.current = args;
    clearTimeout(functionTimeoutHandler.current);

    if (leadingCall.current) {
      leadingCall.current = false;
    }

    if (!functionTimeoutHandler.current && leading && !leadingCall.current) {
      debouncedFunction.current.apply(debouncedFunction, args);
      leadingCall.current = true;
    }

    functionTimeoutHandler.current = setTimeout(function () {
      var shouldCallFunction = true;

      if (leading && leadingCall.current) {
        shouldCallFunction = false;
      }

      cancelDebouncedCallback();

      if (!isComponentUnmounted.current && trailing && shouldCallFunction) {
        debouncedFunction.current.apply(debouncedFunction, args);
      }
    }, delay);

    if (maxWait && !maxWaitHandler.current && trailing) {
      maxWaitHandler.current = setTimeout(function () {
        var args = maxWaitArgs.current;
        cancelDebouncedCallback();

        if (!isComponentUnmounted.current) {
          debouncedFunction.current.apply(null, args);
        }
      }, maxWait);
    }
  }, [maxWait, delay, cancelDebouncedCallback, leading, trailing]);

  var callPending = function callPending() {
    if (!functionTimeoutHandler.current) {
      return;
    }

    debouncedFunction.current.apply(null, maxWaitArgs.current);
    cancelDebouncedCallback();
  };

  return [debouncedCallback, cancelDebouncedCallback, callPending];
};

export default useDebouncedCallback;