import React, { useRef, useEffect, useState, useCallback, ReactNode } from 'react';
import { useSpring, animated } from '@react-spring/web';
import { useDrag } from '@use-gesture/react';
import clsx from 'clsx';

interface InfiniteCarouselProps {
  children: React.ReactNode;
  height: string;
  speed?: number;
  gap?: number;
  overlay?: ReactNode;
  overlayFunc?: (index: number) => void;
  overlayClassName?: string;
}

const InfiniteCarousel: React.FC<InfiniteCarouselProps> = ({
  children,
  height,
  speed = 20000,
  gap = 16,
  overlay = undefined,
  overlayFunc = undefined,
  overlayClassName = ""
}) => {
  const containerRef = useRef<HTMLDivElement>(null);
  const [containerWidth, setContainerWidth] = useState<number>(0);
  const itemCount = React.Children.count(children);
  const [isDragging, setIsDragging] = useState<boolean>(false);
  const [itemWidth, setItemWidth] = useState<number>(0);
  const [isInfinite, setIsInfinite] = useState<boolean>(true);
  const dragStartX = useRef<number>(0);

  useEffect(() => {
    const handleResize = () => {
      if (containerRef.current) {
        const containerWidth = containerRef.current.clientWidth;
        setContainerWidth(containerWidth);

        const firstChild = containerRef.current.firstElementChild?.firstElementChild as HTMLElement;
        if (firstChild) {
          const childWidth = firstChild.offsetWidth;
          setItemWidth(childWidth);

          const totalContentWidth = (childWidth + gap) * itemCount - gap;
          setIsInfinite(totalContentWidth > containerWidth);
        }
      }
    };

    handleResize();
    window.addEventListener('resize', handleResize);

    return () => {
      window.removeEventListener('resize', handleResize);
    };
  }, [gap, itemCount]);

  const totalWidth = (itemWidth + gap) * itemCount;

  const [{ x }, api] = useSpring(() => ({
    from: { x: 0 },
    to: { x: -totalWidth },
    config: { duration: speed },
    loop: true,
  }));

  const startAnimation = useCallback(() => {
    if (isInfinite) {
      api.start({
        from: { x: x.get() },
        to: { x: x.get() - totalWidth },
        config: { duration: speed },
        loop: true,
      });
    } else {
      api.stop();
      api.start({ x: 0, immediate: true });
    }
  }, [api, x, totalWidth, speed, isInfinite]);

  useEffect(() => {
    if (containerWidth > 0 && !isDragging) {
      startAnimation();
    }
  }, [containerWidth, isDragging, startAnimation]);

  const bind = useDrag(({ down, movement: [mx], first }) => {
    if (!isInfinite) return;
    if (first) {
      setIsDragging(true);
      api.stop();
      dragStartX.current = x.get();
    }
    if (down) {
      let newX = dragStartX.current + mx;
      newX = ((newX % totalWidth) + totalWidth) % totalWidth;
      if (newX > 0) newX -= totalWidth;
      api.start({ x: newX, immediate: true });
    } else {
      setIsDragging(false);
      startAnimation();
    }
  }, {
    filterTaps: true,
    from: () => [x.get(), 0],
  });

  return (
    <div
      className="overflow-hidden relative select-none"
      style={{ height, touchAction: 'pan-y' }}
      ref={containerRef}
      {...(isInfinite ? bind() : {})}
    >
      <animated.div
        className="flex"
        style={{
          display: 'flex',
          willChange: 'transform',
          transform: isInfinite
            ? x.to((x) => `translate3d(${x}px, 0, 0)`)
            : 'none',
          gap: `${gap}px`,
          width: 'fit-content',
        }}
      >
        {React.Children.map(children, (child, i) => (
          <div
            key={i}
            className="flex-shrink-0 relative"
          >
            {overlay !== undefined && overlayFunc !== undefined && (
              <div className={clsx('absolute', overlayClassName)} onClick={() => overlayFunc(i)}>
                {overlay}
              </div>
            )}
            <div
              className="select-none"
              style={{ pointerEvents: 'none' }}
            >
              {child}
            </div>
          </div>
        ))}
        {isInfinite && React.Children.map(children, (child, i) => (
          <div
            key={`clone-${i}`}
            className="flex-shrink-0 relative"
          >
            {overlay !== undefined && overlayFunc !== undefined && (
              <div className={clsx('absolute', overlayClassName)} onClick={() => overlayFunc(i)}>
                {overlay}
              </div>
            )}
            <div
              className="select-none"
              style={{ pointerEvents: 'none' }}
            >
              {child}
            </div>
          </div>
        ))}
      </animated.div>
    </div>
  );
};

export default InfiniteCarousel;