import * as React from 'react';
import { CSSProperties, useContext, useRef, MouseEvent } from 'react';
import Arrow from '../../../Icons/Arrows/Arrow';
import { StoreContext } from '../../../../services/store';
import { spotlightCarouselTransitionDurationSec } from '../../../consts';
import { useAccessibilityMessage } from '../../../../hooks/useAccessibilityMessage';
import {
  useUniformCenteredConfig,
  useSpotlightLayout,
  useSpotlightHeight,
  useSpotlightNavigation,
} from './hooks';
import type { ISettings } from '../../../../types';
import {
  getCarouselMovementMessage,
  getSrOnlyStyle,
  normalizeIndex,
} from '../shared/accessibility';

interface Props<T> {
  items: T[];
  contentSize: ISettings['content_size'];
  style?: CSSProperties;
}

const SpotlightCarousel = (props: Props<JSX.Element>) => {
  const context = useContext(StoreContext);
  const slideRef = useRef<HTMLDivElement>(null);
  const containerRef = useRef<HTMLDivElement>(null);
  const outerRef = useRef<HTMLDivElement>(null);
  const { thumbnail_shape: thumbnailShape = 'square', content_size: contentSize } =
    context.data.settings;

  const itemCount = props.items.length;
  const { accessibilityMessage, announce } = useAccessibilityMessage();
  const visibleContentSize = Math.max(props.contentSize, 1);

  const { contentSizeNotReached, partialItemWidth, cloneCount } = useUniformCenteredConfig(
    itemCount,
    visibleContentSize,
    context.setStoreState,
  );

  const { constrainedWidgetWidth, isCompact, compactStyle, carouselDimensions, arrowSize } =
    useSpotlightLayout({
      context: {
        widgetWidth: context.widgetWidth,
        contentSize,
        isMobile: context.isMobile,
        hideArrows: context.config.hideArrows || false,
      },
      config: { contentSize: visibleContentSize, itemCount, partialItemWidth },
      refs: { slideRef },
    });

  const { enforcedMinHeight } = useSpotlightHeight({
    refs: { outerRef },
    config: { thumbnailShape, contentSize, itemCount },
    layout: { isCompact, constrainedWidgetWidth, widgetWidth: context.widgetWidth },
  });

  const { activeIndex, handleSlide, getTranslateX, skipTransition } = useSpotlightNavigation({
    config: { itemCount, contentSize: visibleContentSize, cloneCount, contentSizeNotReached },
    refs: { slideRef, containerRef },
    state: { itemWidth: carouselDimensions.itemWidth, currentPosition: context.currentPosition },
    actions: { setStoreState: context.setStoreState },
  });

  const onArrowClick = (
    event: MouseEvent<HTMLButtonElement> | undefined,
    direction: 'left' | 'right',
  ) => {
    const nextIndex = activeIndex + (direction === 'left' ? -1 : 1);
    const normalizedIndex = normalizeIndex(nextIndex, itemCount);
    announce(getCarouselMovementMessage(normalizedIndex, itemCount));

    handleSlide(direction);

    if (event) {
      context.triggerEvent(
        'carouselArrowClicked',
        {
          position: activeIndex,
          originalEvent: event.nativeEvent,
          direction,
        },
        context.data.id,
      );
    }
  };

  const renderSlides = () => {
    const { itemWidth } = carouselDimensions;
    if (cloneCount === 0) {
      return props.items.map((item, i) => renderSlide(item, i, itemWidth));
    }

    const cloneStart = props.items
      .slice(props.items.length - cloneCount)
      .map((item, i) => renderSlide(item, i - cloneCount, itemWidth));
    const realSlides = props.items.map((item, i) => renderSlide(item, i, itemWidth));
    const cloneEnd = props.items
      .slice(0, cloneCount)
      .map((item, i) => renderSlide(item, props.items.length + i, itemWidth));
    return [...cloneStart, ...realSlides, ...cloneEnd];
  };

  const renderSlide = (item: JSX.Element, index: number, width: number) => {
    const isClone = index < 0 || index >= itemCount;
    const cloneAccessibilityProps = isClone
      ? { 'aria-hidden': true, 'data-carousel-clone': 'true' }
      : {};

    return (
      <div
        key={`slide-${index}`}
        style={{
          ...getSlideItemStyle(width),
          width,
          position: 'relative',
        }}
        {...cloneAccessibilityProps}
      >
        {item}
      </div>
    );
  };

  const renderDesktopCarousel = () => {
    // Uniform center mode: no arrows, no navigation
    const showArrows = itemCount > 1 && !context.config.hideArrows && !contentSizeNotReached;
    const hasContentSizeNotReachedWithoutClones =
      cloneCount === 0 && itemCount < visibleContentSize;
    const renderedItemWidth =
      carouselDimensions.itemWidth + getSlidePadding(carouselDimensions.itemWidth) * 2;
    // Shift items so the empty space is split evenly left/right
    const contentSizeNotReachedOffset = hasContentSizeNotReachedWithoutClones
      ? ((visibleContentSize - itemCount) * renderedItemWidth) / 2
      : 0;
    const leftPosition = hasContentSizeNotReachedWithoutClones
      ? contentSizeNotReachedOffset
      : getTranslateX();

    return (
      <>
        <Arrow
          direction='left'
          disabled={false} // Infinite scroll
          onClick={(e) => onArrowClick(e, 'left')}
          show={showArrows}
          classPrefix={context.config.classPrefix}
          size={arrowSize}
          ariaLabel='Previous posts'
          customArrow={context.config.arrowLeft}
        />

        <div style={desktopSlideStyle} ref={slideRef}>
          <div
            ref={containerRef}
            style={{
              ...desktopSlideContainerStyle,
              transform: `translateX(${leftPosition}px)`,
              ...(skipTransition ? { transition: 'none' } : {}),
            }}
          >
            {renderSlides()}
          </div>
        </div>

        <Arrow
          direction='right'
          disabled={false} // Infinite scroll
          onClick={(e) => onArrowClick(e, 'right')}
          show={showArrows}
          classPrefix={context.config.classPrefix}
          size={arrowSize}
          ariaLabel='Next posts'
          customArrow={context.config.arrowRight}
        />
      </>
    );
  };

  return (
    <div
      className={`${context.config.classPrefix}-layout-carousel`}
      style={{
        ...carouselStyle,
        ...props.style,
        ...(compactStyle ?? {}),
        minHeight: `${enforcedMinHeight}px`,
      }}
      ref={outerRef}
    >
      <div aria-live='polite' aria-atomic='true' style={getSrOnlyStyle()}>
        {accessibilityMessage}
      </div>
      {renderDesktopCarousel()}
    </div>
  );
};

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

const desktopSlideStyle: CSSProperties = {
  flex: 1,
  height: 'auto',
  overflow: 'hidden',
  position: 'relative',
  display: 'flex',
  alignItems: 'center',
  paddingTop: '20px',
  paddingBottom: '20px',
};

const desktopSlideContainerStyle: CSSProperties = {
  position: 'relative',
  height: 'auto',
  display: 'flex',
  alignItems: 'center',
  willChange: 'transform',
  transition: `transform ${spotlightCarouselTransitionDurationSec}s ease`,
};

const slideItemStyle: CSSProperties = {
  display: 'inline-block',
  height: 'auto',
  flexShrink: 0,
  boxSizing: 'border-box',
  isolation: 'auto', // to prevent stacking issues
};

const getSlidePadding = (itemWidth: number) => {
  if (itemWidth < 80) {
    return 2;
  }
  if (itemWidth < 120) {
    return 3;
  }
  if (itemWidth < 160) {
    return 4;
  }
  return 5;
};

const getSlideItemStyle = (itemWidth: number): CSSProperties => ({
  ...slideItemStyle,
  padding: `0 ${getSlidePadding(itemWidth)}px`,
});

export default SpotlightCarousel;
