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 { title: 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 [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]],
  );

  useEffect(() => {
    const sliderRefCurrent = sliderRef.current;

    sliderRefCurrent?.addEventListener('transitionend', () => {
      setTransiting(false);
      setCurrentSliding(null);
    });

    return () => {
      sliderRefCurrent?.removeEventListener('transitionend', () => {
        setTransiting(false);
        setCurrentSliding(null);
      });
    };
  }, []);

  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: nextSlide,
    onSwipedRight: prevSlide,
    trackMouse: true,
    preventScrollOnSwipe: true,
    touchEventOptions: { passive: false },
  });

  return (
    <div className={'flex flex-col relative ' + className}>
      <div className="overflow-hidden sm:-mx-6 -mx-4 h-full">
        <div {...handlers} className="h-full">
          <div
            className={`flex items-${alignItems} gap-x-4 -mr-6 select-none`}
            style={{
              minHeight: minHeightScaled,
              transform: `translateX(${sliderOffset}px)`,
              transitionDuration: `${sliderOffset !== 0 && sliderOffset !== 2 * baseoffset ? 1 : 0}s`,
            }}
            ref={sliderRef}
          >
            {order.map((slide, index) => (
              <motion.div
                key={
                  slide.title +
                  (index < 1 ? '123' : index > slides.length ? '456' : '')
                }
                initial={
                  currentSliding === 'next' && index === 0
                    ? { minWidth: minWidthScaled, minHeight: minHeightScaled }
                    : index === 3
                      ? { minWidth, minHeight }
                      : {}
                }
                transition={{ duration: 1, 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
        slide={slide}
        onLeftClick={prevSlide}
        onRightClick={nextSlide}
        slidesCount={slides.length}
        width={width >= 640 ? 132 : ((width - 32) / 328) * 196}
        height={width >= 640 ? 66 : 58}
        className={
          'absolute ' +
          (controlsPosition === 'top'
            ? 'top-[75px]'
            : 'bottom-0 sm:self-end self-center')
        }
      />
    </div>
  );
}
