import { useCallback, useEffect, useMemo, useRef, useState } from "react";

type Timer = {
  started: number | null;
  lastInterval: number | null;
  timeLeft: number;
  timeToCount: number;
  requestId: number;
};

const useCountDown = (
  timeToCount = 60 * 1000,
  interval = 1000,
): [
  number,
  {
    start: (ttc?: number) => void;
    pause: () => void;
    resume: () => void;
    reset: () => void;
  },
] => {
  const [timeLeft, setTimeLeft] = useState(0);
  const timer = useRef<Timer>({
    started: null,
    lastInterval: null,
    timeLeft: 0,
    timeToCount: 0,
    requestId: 0,
  });

  const run = (ts: number) => {
    if (!timer.current.started) {
      timer.current.started = ts;
      timer.current.lastInterval = ts;
    }

    const localInterval = Math.min(
      interval,
      timer.current.timeLeft || Infinity,
    );
    if (timer.current.lastInterval && ts - timer.current.lastInterval >= localInterval) {
      timer.current.lastInterval += localInterval;
      setTimeLeft((timeLeft) => {
        timer.current.timeLeft = timeLeft - localInterval;
        return timer.current.timeLeft;
      });
    }

    if (ts - timer.current.started < timer.current.timeToCount) {
      timer.current.requestId = window.requestAnimationFrame(run);
    } else {
      timer.current = {
        started: null,
        lastInterval: null,
        timeLeft: 0,
        timeToCount: 0,
        requestId: 0,
      };
      setTimeLeft(0);
    }
  };

  const start = useCallback(
    (ttc) => {
      window.cancelAnimationFrame(timer.current.requestId);

      const newTimeToCount = ttc !== undefined ? ttc : timeToCount;
      timer.current.started = null;
      timer.current.lastInterval = null;
      timer.current.timeToCount = newTimeToCount;
      timer.current.requestId = window.requestAnimationFrame(run);

      setTimeLeft(newTimeToCount);
    },
    // eslint-disable-next-line react-hooks/exhaustive-deps
    [],
  );

  const pause = useCallback(() => {
    window.cancelAnimationFrame(timer.current.requestId);
    timer.current.started = null;
    timer.current.lastInterval = null;
    timer.current.timeToCount = timer.current.timeLeft;
  }, []);

  const resume = useCallback(
    () => {
      if (!timer.current.started && timer.current.timeLeft > 0) {
        window.cancelAnimationFrame(timer.current.requestId);
        timer.current.requestId = window.requestAnimationFrame(run);
      }
    },
    // eslint-disable-next-line react-hooks/exhaustive-deps
    [],
  );

  const reset = useCallback(() => {
    if (timer.current.timeLeft) {
      window.cancelAnimationFrame(timer.current.requestId);
      timer.current = {
        started: null,
        lastInterval: null,
        timeLeft: 0,
        timeToCount: 0,
        requestId: 0,
      };
      setTimeLeft(0);
    }
  }, []);

  const actions = useMemo(
    () => ({ start, pause, resume, reset }), // eslint-disable-next-line react-hooks/exhaustive-deps
    [],
  );

  useEffect(() => {
    return () => window.cancelAnimationFrame(timer.current.requestId);
  }, []);

  return [timeLeft, actions];
};

export default useCountDown;
