import { useRef, useState } from 'react';

type ArrowPosition = 'lt' | 'ct' | 'rt';

interface AnimationProps {
  element: HTMLElement;
  targetScroll: number;
  duration: number;
  direction?: 'lt' | 'rt';
  multiply?: number;
  offset?: number;
  callback?: (position: ArrowPosition) => void;
}

const animateHorizontalScroll = ({
  element,
  duration,
  targetScroll,
  multiply = 1,
  direction,
  callback,
  offset = 0,
}: AnimationProps) => {
  const start = element.scrollLeft;
  const distance = (targetScroll - start) * multiply;
  const startTime = performance.now();

  function easeOutSine(x: number): number {
    return Math.sin((x * Math.PI) / 2);
  }

  function scrollStep(currentTime: number): void {
    const timeElapsed = currentTime - startTime;
    const progress = Math.min(timeElapsed / duration, 1);
    const easing = easeOutSine(progress);
    const step = start + distance * easing;
    const canScroll =
      (direction === 'rt' ? element.scrollLeft < step : element.scrollLeft > step) || !direction;

    if (step > 0 && canScroll) {
      element.scrollTo(step, 0);
    }

    if (timeElapsed < duration) {
      requestAnimationFrame(scrollStep);
    }

    // End of scrolling container
    else if (element.scrollLeft + offset === element.scrollWidth) {
      callback?.('rt');
    }
    // Begin of scrolling container
    else if (element.scrollLeft <= 1) {
      callback?.('lt');
    }
    // Somewhere in the middle
    else {
      callback?.('ct');
    }
  }

  requestAnimationFrame(scrollStep);
};

export const useGrabAndScroll = (
  enabled: boolean,
  chunkWidth: number,
  speed: number = 400,
  offset: number = 0,
) => {
  const containerRef = useRef<HTMLDivElement>(null);
  const [leftArrow, setLeftArrow] = useState(false);
  const [rightArrow, setRightArrow] = useState(true);

  const handleArrows = (position: ArrowPosition) => {
    switch (position) {
      case 'lt':
        setLeftArrow(false);
        setRightArrow(true);
        break;

      case 'rt':
        setLeftArrow(true);
        setRightArrow(false);
        break;

      default:
        setLeftArrow(true);
        setRightArrow(true);
    }
  };

  const animationTrigger = () => {
    if (enabled && containerRef.current) {
      const dist = (containerRef?.current?.scrollLeft || 0) / chunkWidth;
      const targetScroll = chunkWidth * (Math.floor(dist) + (dist % 1 > 0.5 ? 1 : 0));
      animateHorizontalScroll({
        element: containerRef.current,
        targetScroll,
        duration: speed,
        offset,
        callback: handleArrows,
      });
    }
  };

  const onNext = (cardsToSlide: number) => {
    if (containerRef.current) {
      const targetScroll = containerRef.current.scrollLeft + chunkWidth;
      animateHorizontalScroll({
        element: containerRef.current,
        targetScroll,
        duration: speed,
        direction: 'rt',
        multiply: cardsToSlide,
        offset,
        callback: handleArrows,
      });
    }
  };

  const onPrevious = (cardsToSlide: number) => {
    if (containerRef.current) {
      const targetScroll = containerRef.current.scrollLeft - chunkWidth;
      animateHorizontalScroll({
        element: containerRef.current,
        targetScroll,
        duration: speed,
        direction: 'lt',
        multiply: cardsToSlide,
        offset,
        callback: handleArrows,
      });
    }
  };

  return {
    containerRef,
    leftArrow,
    rightArrow,
    animationTrigger,
    onNext,
    onPrevious,
  };
};
