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 './SliderControls';

export function SliderWithScaling<T extends { img: string }>({
  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 [sliderOffset, setSliderOffset] = useState(baseoffset);
  const [transiting, setTransiting] = useState(false);
  const [currentSliding, setCurrentSliding] = useState<
    'prev' | 'next' | null
  >();

  const sliderRef = useRef<HTMLDivElement>(null);
  const itemRef = useRef<HTMLDivElement>(null);
  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(2 * baseoffset);
          return [state[state.length - 3], ...state.slice(0, -1)];
        case 'next':
          setSlide(slide => (slide === slides.length - 1 ? 0 : slide + 1));
          setSliderOffset(0);
          return [...state.slice(1), state[2]];
        default:
          return state;
      }
    },
    [slides[slides.length - 1], ...slides, slides[0]],
  );

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

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

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

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

  return (
    <div className={'flex flex-col relative ' + className}>
      <div className="h-full -mx-4 overflow-hidden sm:-mx-6">
        <div {...handlers} className="h-full">
          <div
            className={`flex items-${alignItems} gap-x-4 -mr-6 select-none`}
            onTransitionEnd={() => {
              setTransiting(false);
            }}
            style={{
              minHeight: minHeightScaled,
              transform: `translateX(${sliderOffset}px)`,
              transitionDuration: `${sliderOffset !== 0 && sliderOffset !== 2 * baseoffset ? 1 : 0}s`,
            }}
            ref={sliderRef}
          >
            {order.map((slide, index) => {
              return (
                <motion.div
                  key={
                    slide.img +
                    (index < 1 ? '123' : index > slides.length ? '456' : '')
                  }
                  initial={
                    currentSliding === 'next' && index === 0
                      ? { minWidth: minWidthScaled, minHeight: minHeightScaled }
                      : index === 3
                        ? { minWidth, minHeight }
                        : {}
                  }
                  transition={{ duration: 0.9, type: 'just' }}
                  animate={
                    index === 1
                      ? {
                          minWidth: minWidthScaled,
                          minHeight: minHeightScaled,
                        }
                      : {
                          minWidth,
                          minHeight,
                          transition: { duration: index === 3 ? 0.0001 : 1 },
                        }
                  }
                  className={'pointer-events-none ' + childClassName}
                >
                  <SlideElement
                    {...slide}
                    ref={index === 1 && !transiting ? itemRef : null}
                  />
                </motion.div>
              );
            })}
          </div>
        </div>
      </div>
      <SliderControls
        ref={controlsRef}
        slide={slide}
        onLeftClick={prevSlide}
        onRightClick={nextSlide}
        slidesCount={slides.length}
        className={
          'absolute ' +
          (controlsPosition === 'top'
            ? 'top-[75px] sm:self-end'
            : 'lg:bottom-32 bottom-0 sm:self-end self-center')
        }
      />
    </div>
  );
}
