import { useWindowWidth } from '../hooks/useWindowWidth';
import { motion } from 'framer-motion';
import { ReactNode, useEffect, useReducer, useRef, useState } from 'react';
import { useSwipeable } from 'react-swipeable';
import { SliderControls } from '../components/Main/SliderControls';

export function SliderWithScaling<T extends { title: string; id: number }>({
  slides,
  SlideElement,
  className = '',
  childClassName = '',
  alignItems = 'start',
  slideSizes: [minWidth, minHeight, minWidthScaled, minHeightScaled],
  controlsPosition,
}: {
  slides: T[];
  SlideElement: (_: T) => ReactNode;
  className?: string;
  childClassName?: string;
  alignItems?: 'start' | 'center' | 'end';
  slideSizes: [string, string, string, string];
  controlsPosition: 'top' | 'bottom';
}) {
  const width = useWindowWidth();
  const baseoffset =
    width >= 1024
      ? (-width / 1600) * 507 + 8
      : (-width * +minWidth.slice(0, -2)) / 100 - 8;

  const [slide, setSlide] = useState(0);
  const [transiting, setTransiting] = useState(false);
  const [sliderOffset, setSliderOffset] = useState(baseoffset);

  const controlsRef = useRef<{ left: () => void; right: () => void }>({
    left: () => {},
    right: () => {},
  });

  const [order, dispatch] = useReducer(
    (state: typeof slides, action: 'prev' | 'next') => {
      setTransiting(true);
      switch (action) {
        case 'prev':
          setSlide(slide => (slide === 0 ? slides.length - 1 : slide - 1));
          setSliderOffset(3 * baseoffset);
          return [
            { ...state[state.length - 5], id: Math.random() },
            ...state.slice(0, -1),
          ];
        case 'next':
          setSlide(slide => (slide + 1) % slides.length);
          setSliderOffset(baseoffset);
          return [...state.slice(1), { ...state[4], id: Math.random() }];
        default:
          return state;
      }
    },
    [
      { ...slides[slides.length - 2], id: Math.random() },
      { ...slides[slides.length - 1], id: Math.random() },
      ...slides,
      { ...slides[0], id: Math.random() },
      { ...slides[1], id: Math.random() },
    ],
  );

  useEffect(() => {
    setSliderOffset(2 * baseoffset);
  }, [baseoffset, order, slide]);

  const handlers = useSwipeable({
    onSwipedLeft: controlsRef.current.right,
    onSwipedRight: controlsRef.current.left,
    trackMouse: true,
    preventScrollOnSwipe: true,
    touchEventOptions: { passive: false },
  });

  function nextSlide() {
    if (!transiting) {
      dispatch('next');
    }
  }

  function prevSlide() {
    if (!transiting) {
      dispatch('prev');
    }
  }

  return (
    <div className={'flex flex-col relative ' + className}>
      <div className="lg:-mx-10 sm:-mx-6 overflow-hidden -mx-4 h-full">
        <div {...handlers} className="h-full">
          <div
            className={`items-${alignItems} flex gap-x-4 -mr-6 select-none`}
            onTransitionEnd={() => {
              setTransiting(false);
            }}
            style={{
              minHeight: minHeightScaled,
              transform: `translateX(${sliderOffset}px)`,
              transitionDuration: `${sliderOffset !== baseoffset && sliderOffset !== 3 * baseoffset ? 1 : 0}s`,
            }}
          >
            {order.map((currentSlide, index) => (
              <motion.div
                key={currentSlide.id}
                initial={
                  index === 1
                    ? {
                        minWidth: minWidthScaled,
                        minHeight: minHeightScaled,
                      }
                    : { minWidth, minHeight }
                }
                animate={
                  index === 2
                    ? {
                        minWidth: minWidthScaled,
                        minHeight: minHeightScaled,
                      }
                    : {
                        minWidth,
                        minHeight,
                      }
                }
                transition={{ duration: 1, type: 'just' }}
                className={'cursor-pointer ' + childClassName}
              >
                <SlideElement {...currentSlide} />
              </motion.div>
            ))}
          </div>
        </div>
      </div>
      <SliderControls
        slide={slide}
        ref={controlsRef}
        onLeftClick={prevSlide}
        onRightClick={nextSlide}
        slidesCount={slides.length}
        width={width >= 640 ? 132 : (width / 360) * 196}
        height={width >= 640 ? 66 : 58}
        className={
          'max-lg:absolute ' +
          (controlsPosition === 'top'
            ? 'top-[70px]'
            : 'lg:bottom-16 -bottom-4 sm:bottom-10 max-sm:self-center self-end max-sm:w-full')
        }
      />
    </div>
  );
}
