type Func = (...args: unknown[]) => void;
type ThrottledFunction = Func & { clear: () => void };

/**
 * Creates a throttled version of a function that only invokes the function at most once per specified time limit.
 * @param func - The function to throttle.
 * @param limit - The time limit in milliseconds.
 * @returns A throttled function with a `clear` method.
 */
export const throttle = (func: Func, limit: number): ThrottledFunction => {
  let lastFunc: number | undefined;
  let lastRan: number | undefined;

  const fn: ThrottledFunction = (...args: unknown[]) => {
    if (!lastRan) {
      func(...args);
      lastRan = Date.now();
    } else {
      clearTimeout(lastFunc);
      lastFunc = window.setTimeout(
        () => {
          if (Date.now() - lastRan! >= limit) {
            func(...args);
            lastRan = Date.now();
          }
        },
        limit - (Date.now() - lastRan!),
      );
    }
  };

  fn.clear = () => clearTimeout(lastFunc);

  return fn;
};

export function debounce<T extends (...args: never[]) => void>(
  func: T,
  delay: number,
): { (...args: Parameters<T>): void; clear: () => void } {
  let timeoutId: number | undefined;

  const debouncedFunction = (...args: Parameters<T>) => {
    if (timeoutId) {
      clearTimeout(timeoutId);
    }
    timeoutId = window.setTimeout(() => func(...args), delay);
  };

  debouncedFunction.clear = () => {
    if (timeoutId) {
      clearTimeout(timeoutId);
      timeoutId = undefined;
    }
  };

  return debouncedFunction;
}
