import React, { useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import classNames from 'clsx';

import { TYPE_B, videoPropTypes } from '../constants';
import { SPACING } from '@/mui/constants';

import { getNumberInRange, isInRange } from '@/modules/@common/helpers/number';

import Arrow from './Arrow/Arrow';
import Slide from './Slide/Slide';

import styles from './Carousel.module.scss';

function CarouselWidget({ slidesToShow = 3, slidesToScroll = 3, slideDimensions = TYPE_B, ...props }) {
  const [indexPosition, setIndexPosition] = useState(0);
  const [translateShift, setTranslateShift] = useState(0);
  const [start, setStart] = useState({ x: 0, y: 0, canceled: false });
  const [draggableShiftPosition, setDraggableShiftPosition] = useState(0);
  const [prevButton, setPrevState] = useState(false);
  const [nextButton, setNextState] = useState(false);
  const [animated, setAnimatedState] = useState(true);

  const padding = SPACING * 3;
  const shiftSize = slideDimensions.width + padding;
  const carouselWidth = props.playlist.length * shiftSize - padding;
  const indexStart = Math.max(0, Math.floor(Math.abs(translateShift) / shiftSize));
  const visibleIndexStart = Math.max(0, indexStart - slidesToShow);
  const visibleIndexEnd = indexStart + slidesToShow + slidesToShow;

  const changeSlide = (delta) => {
    const width = props.playlist.length - slidesToShow;

    const nextIndex = getNumberInRange(indexPosition + delta, 0, width);

    setAnimatedState(true);
    setIndexPosition(nextIndex);
  };

  useEffect(() => {
    let showPrev = false;
    let showNext = false;

    if (carouselWidth > 0) {
      showPrev = !!indexPosition;
      showNext = indexPosition * shiftSize < carouselWidth - slidesToShow * shiftSize;
    }

    setPrevState(showPrev);
    setNextState(showNext);
    setTranslateShift(
      getNumberInRange(indexPosition * shiftSize, 0, Math.max(0, carouselWidth - slideDimensions.width)),
    );
  });

  useEffect(() => {
    setIndexPosition(0);
  }, [props.playlist.map(({ title }) => title).join('|')]);

  return (
    <div
      onTouchStart={(event) =>
        setStart({
          x: event.touches[0].screenX,
          y: event.touches[0].screenY,
          canceled: false,
        })
      }
      onTouchMove={(event) => {
        if (!start.canceled) {
          setDraggableShiftPosition(event.touches[0].screenX - start.x);
          setAnimatedState(false);
        }
      }}
      onTouchEnd={(event) => {
        if (!start.canceled) {
          const delta = start.x - event.changedTouches[0].screenX;

          if (Math.abs(delta) >= Math.floor(window.devicePixelRatio || 0) * 25 && Math.abs(delta) < shiftSize) {
            changeSlide(Math.sign(delta));
          } else if (Math.abs(delta) >= shiftSize) {
            changeSlide(Math.round(delta / shiftSize));
          }
        }

        setAnimatedState(true);
        setDraggableShiftPosition(0);
      }}
      onTouchCancel={() => {
        setAnimatedState(true);
        setDraggableShiftPosition(0);
      }}
      style={{
        minWidth: shiftSize * slidesToShow - padding,
        maxWidth: shiftSize * slidesToShow - padding,
      }}
      className={styles.Wrapper}
    >
      <div className={styles.Slider}>
        <div className={styles.List}>
          <div
            className={classNames(styles.Track, {
              [styles.Track___animated]: animated,
            })}
            style={{
              gap: padding,
              width: carouselWidth,
              transform: `translate3d(${translateShift * -1 + draggableShiftPosition}px, 0, 0)`,
              paddingLeft: visibleIndexStart * shiftSize,
            }}
          >
            {props.playlist.map((video, index) => {
              if (!isInRange(index, visibleIndexStart, visibleIndexEnd)) {
                return null;
              }

              return (
                <div
                  key={video.title}
                  className={styles.Slide}
                  style={{
                    width: slideDimensions.width,
                  }}
                >
                  <Slide
                    video={video}
                    width={slideDimensions.width}
                    height={slideDimensions.height}
                    isSmallScreen={false}
                  />
                </div>
              );
            })}
          </div>
        </div>
        {prevButton && (
          <Arrow
            onClick={() => changeSlide(-1 * Math.min(slidesToScroll, slidesToShow))}
            top={slideDimensions.height / 2}
          />
        )}
        {nextButton && (
          <Arrow
            isNext
            onClick={() => changeSlide(Math.min(slidesToScroll, slidesToShow))}
            top={slideDimensions.height / 2}
          />
        )}
      </div>
    </div>
  );
}

CarouselWidget.displayName = 'CarouselWidget';

CarouselWidget.propTypes = {
  playlist: PropTypes.arrayOf(videoPropTypes).isRequired,
  slidesToShow: PropTypes.number,
  slidesToScroll: PropTypes.number,
  slideDimensions: PropTypes.shape({
    width: PropTypes.number,
    height: PropTypes.number,
  }),
};

export default CarouselWidget;
