import * as React from 'react';
import { CSSProperties, useContext, useRef, useLayoutEffect, isValidElement, cloneElement, ReactElement } from 'react';
import { StoreContext } from '../../../../services/store';
import { IMedia } from '../../../../types';
import { useMobileCarouselDimensions, useMobileScrollSync } from './hooks';
import { getCarouselCloneAccessibilityProps } from '../shared/accessibility';

interface Props<T> {
  items: T[];
  nbElems: number;
  style?: CSSProperties;
  onPostChange: (post: IMedia | null) => void;
}

const MobileCarousel = (props: Props<JSX.Element>) => {
  const context = useContext(StoreContext);
  const scrollContainerRef = useRef<HTMLDivElement>(null);

  const itemCount = props.items.length;
  const visibleContentSize = Math.max(props.nbElems, 1);

  // Use clones only when there is more than one item.
  // Clone enough items to fill the visible viewport and support seamless wrapping.
  const needsCloning = itemCount > 1;
  const cloneCount = needsCloning ? Math.max(visibleContentSize, itemCount) : 0;

  const { itemWidthPercentage } = useMobileCarouselDimensions(visibleContentSize);

  const { handleScroll } = useMobileScrollSync({
    scrollContainerRef,
    itemCount,
    cloneCount,
    visibleContentSize,
    setStoreState: context.setStoreState,
    triggerEvent: context.triggerEvent,
    widgetId: context.data.id,
  });

  // Render slides and optional clone buffer for infinite scrolling.
  const renderSlides = () => {
    if (!needsCloning) {
      return props.items.map((item, i) => renderSlide(item, i, i));
    }

    // Clone items at the start and end for seamless looping.
    // No spacers are required because scroll-padding keeps the shoulder spacing.
    const cloneStart = props.items.slice(props.items.length - cloneCount).map((item, i) => {
      const idx = props.items.length - cloneCount + i;
      return renderSlide(item, idx, i, 'clone-start');
    });

    const realSlides = props.items.map((item, i) => renderSlide(item, i, cloneCount + i, 'real'));

    const cloneEnd = props.items
      .slice(0, cloneCount)
      .map((item, i) => renderSlide(item, i, cloneCount + props.items.length + i, 'clone-end'));

    return [...cloneStart, ...realSlides, ...cloneEnd];
  };

  const renderSlide = (
    item: JSX.Element,
    originalIndex: number,
    renderIndex: number,
    type?: string,
  ) => {
    const isClone = type === 'clone-start' || type === 'clone-end';
    const cloneAccessibilityProps = getCarouselCloneAccessibilityProps(isClone);
    const slideContent = isValidElement(item)
      ? cloneElement(item as ReactElement<Record<string, unknown>>, {
          isCarouselClone: isClone,
        })
      : item;

    return (
      <div
        key={`mobile-slide-${renderIndex}`}
        style={{
          ...slideItemStyle,
          width: `${itemWidthPercentage}%`,
          flexShrink: 0,
          // Center snap for a single visible item, otherwise align slides to the start.
          scrollSnapAlign: visibleContentSize === 1 ? 'center' : 'start',
          // Prevent fast swipes from skipping across multiple slides in one gesture.
          scrollSnapStop: 'always',
          // Clone slides should not intercept interactions.
          ...(isClone ? { pointerEvents: 'none' } : {}),
        }}
        {...cloneAccessibilityProps}
      >
        {slideContent}
      </div>
    );
  };

  // Initialize the starting scroll offset so the first real slide appears with the expected shoulder spacing.
  useLayoutEffect(() => {
    if (!scrollContainerRef.current || !needsCloning) return;

    const container = scrollContainerRef.current;
    const { scrollWidth, clientWidth } = container;
    const totalItems = itemCount + 2 * cloneCount;
    const itemWidth = scrollWidth / totalItems;

    // Apply the start position immediately, without smooth animation.
    container.style.scrollBehavior = 'auto';

    if (visibleContentSize === 1) {
      // Single item: center the first real item and let the remaining width form shoulders.
      const scrollPos = itemWidth * cloneCount;
      container.scrollLeft = scrollPos;
    } else {
      // Multi-item: position the first real item so 5% shoulder is visible on the left.
      const firstRealItemPos = itemWidth * cloneCount;
      const shoulderOffset = clientWidth * 0.05; // 5% of viewport
      const scrollPos = firstRealItemPos - shoulderOffset;

      container.scrollLeft = scrollPos;
    }

    // Re-enable smooth scrolling for subsequent user interactions
    requestAnimationFrame(() => {
      if (container) {
        container.style.scrollBehavior = 'smooth';
      }
    });

    // Inject CSS to hide scrollbar in WebKit browsers
    const style = document.createElement('style');
    style.textContent = `
      .${context.config.classPrefix}-mobile-carousel div::-webkit-scrollbar {
        display: none;
      }
    `;
    document.head.appendChild(style);

    // Cleanup: remove injected style on unmount
    const cleanup = () => {
      if (style.parentNode) {
        document.head.removeChild(style);
      }
    };
    // eslint-disable-next-line consistent-return
    return cleanup;
  }, []); // Run once on mount

  return (
    <div
      className={
        `${context.config.classPrefix}-layout-carousel ` +
        `${context.config.classPrefix}-mobile-carousel`
      }
      style={{
        ...carouselStyle,
        ...props.style,
      }}
    >
      <div
        ref={scrollContainerRef}
        onScroll={handleScroll}
        style={{
          ...scrollContainerStyle,
          // Preserve left shoulder spacing for multi-item layouts.
          scrollPaddingLeft: visibleContentSize > 1 ? '5%' : '0',
        }}
      >
        {renderSlides()}
      </div>
    </div>
  );
};

const carouselStyle: CSSProperties = {
  width: '100%',
  display: 'flex',
  position: 'relative',
  minHeight: 'fit-content',
  overflow: 'hidden',
};

const scrollContainerStyle: CSSProperties = {
  display: 'flex',
  overflowX: 'scroll',
  overflowY: 'hidden',
  scrollSnapType: 'x mandatory',
  scrollBehavior: 'auto', // overridden to 'smooth' after initial position is set
  WebkitOverflowScrolling: 'touch',
  scrollbarWidth: 'none', // Firefox
  msOverflowStyle: 'none', // IE/Edge
  width: '100%',
  paddingTop: '20px',
  paddingBottom: '20px',
};

const slideItemStyle: CSSProperties = {
  display: 'flex',
  alignItems: 'center',
  justifyContent: 'center',
  height: 'auto',
  boxSizing: 'border-box',
  position: 'relative',
};

export default MobileCarousel;
