import * as React from 'react';
import { CSSProperties, useContext, useRef, useEffect, MouseEvent } from 'react';
import Arrow from '../../../Icons/Arrows/Arrow';
import { StoreContext } from '../../../../services/store';
import { IMedia } from '../../../../types';
import {
  uniformCarouselTransitionDurationSec,
  ugcZIndex,
} from '../../../consts';
import { useAccessibilityMessage } from '../../../../hooks/useAccessibilityMessage';
import {
  useUniformCenteredConfig,
  useCarouselLayout,
  useInfiniteNavigation,
} from '../shared/hooks';
import { useUniformTranslateX } from '../UniformCarousel/hooks';
import {
  getCarouselMovementMessage,
  getSrOnlyStyle,
  normalizeIndex,
} from '../shared/accessibility';
import { useBuyBoxScrollState } from './hooks';

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

/**
 * This is a compact desktop-only carousel designed to fit inside a product-page "Buy Box" column.
 */
const BuyBoxCarousel = (props: Props<JSX.Element>) => {
  const context = useContext(StoreContext);
  const slideRef = useRef<HTMLDivElement>(null);
  const containerRef = useRef<HTMLDivElement>(null);
  const outerRef = useRef<HTMLDivElement>(null);

  const { hasScrolled, markScrolled } = useBuyBoxScrollState();

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

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

  const { compactStyle, carouselDimensions, arrowSize } = useCarouselLayout({
    context: {
      widgetWidth: context.widgetWidth,
      contentSize,
      isMobile: context.isMobile,
      // Tell the dimension hook there are no side arrows so it does NOT subtract
      // arrow space from the available content width.
      hideArrows: true,
    },
    config: {
      contentSize: visibleContentSize,
      itemCount,
      partialItemWidth: contentSizeNotReached ? 0 : 0.5,
      alwaysPartial: true,
    },
    refs: { slideRef },
  });

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

  const { getTranslateX } = useUniformTranslateX({
    config: { cloneCount },
    state: { itemWidth: carouselDimensions.itemWidth, activeIndex },
  });

  const onArrowClick = (
    event: MouseEvent<HTMLButtonElement> | undefined,
    direction: 'left' | 'right',
  ) => {
    // Reveal the left arrow permanently after the first scroll.
    markScrolled();

    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={{
          ...slideItemStyle,
          width,
          position: 'relative',
        }}
        {...cloneAccessibilityProps}
      >
        {item}
      </div>
    );
  };

  // Arrows are only rendered when there is more than one item, the global
  // hideArrows flag is not set, and the product list is not open.
  const showArrows =
    itemCount > 1 && !context.config.hideArrows && !contentSizeNotReached && !context.post;

  // Left arrow is hidden until the carousel has been scrolled at least once.
  // After the first scroll the loop guarantees previous content always exists.
  const showLeftArrow = showArrows && hasScrolled;
  const showRightArrow = showArrows;

  const leftPosition = getTranslateX();

  return (
    <div
      className={`${context.config.classPrefix}-layout-carousel ${context.config.classPrefix}-layout-buy_box-carousel`}
      style={{
        ...carouselStyle,
        ...props.style,
        ...(compactStyle ?? {}),
      }}
      ref={outerRef}
      // Announce the region role so assistive technologies understand the
      // interactive nature of the component.
      role='region'
      aria-label='Image carousel'
    >
      {/* Screen-reader-only live region for navigation announcements */}
      <div aria-live='polite' aria-atomic='true' style={getSrOnlyStyle()}>
        {accessibilityMessage}
      </div>

      {/* Scrollable track — takes the full container width */}
      <div style={desktopSlideStyle} ref={slideRef}>
        <div
          ref={containerRef}
          style={{
            ...desktopSlideContainerStyle,
            transform: `translateX(${leftPosition}px)`,
            ...(skipTransition ? { transition: 'none' } : {}),
          }}
        >
          {renderSlides()}
        </div>
      </div>

      {/*
       * Overlaid left arrow.
       * Hidden on init; fades in permanently after the first navigation action.
       *
       * Visibility is controlled exclusively by the wrapper (opacity + inert),
       * so Arrow always renders a <button> once arrows are enabled — avoiding
       * the div→button element-type switch that can interrupt the CSS transition.
       */}
      <div
        className={`${context.config.classPrefix}-buy-box-arrow-wrapper ${context.config.classPrefix}-buy-box-arrow-wrapper-left`}
        style={getOverlayArrowWrapperStyle('left', showLeftArrow)}
        aria-hidden={!showLeftArrow}
        {...(!showLeftArrow ? { inert: '' } : {})}
      >
        <Arrow
          direction='left'
          disabled={false}
          onClick={(e) => onArrowClick(e, 'left')}
          show={showArrows}
          classPrefix={context.config.classPrefix}
          size={arrowSize}
          ariaLabel='Previous posts'
          customArrow={context.config.arrowLeft}
        />
      </div>

      {/* Overlaid right arrow — always visible when arrows are enabled */}
      <div
        className={`${context.config.classPrefix}-buy-box-arrow-wrapper ${context.config.classPrefix}-buy-box-arrow-wrapper-right`}
        style={getOverlayArrowWrapperStyle('right', showRightArrow)}
      >
        <Arrow
          direction='right'
          disabled={false}
          onClick={(e) => onArrowClick(e, 'right')}
          show={showArrows}
          classPrefix={context.config.classPrefix}
          size={arrowSize}
          ariaLabel='Next posts'
          customArrow={context.config.arrowRight}
        />
      </div>
    </div>
  );
};

// ---------------------------------------------------------------------------
// Styles
// ---------------------------------------------------------------------------

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

const desktopSlideStyle: CSSProperties = {
  width: '100%',
  height: 'auto',
  overflow: 'hidden',
  position: 'relative',
  display: 'flex',
  alignItems: 'center',
};

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

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

/**
 * Positions an arrow overlay on the left or right edge of the carousel,
 * stretching the full height so the Arrow button can use height: 100%.
 */
const getOverlayArrowWrapperStyle = (
  direction: 'left' | 'right',
  visible: boolean,
): CSSProperties => ({
  position: 'absolute',
  top: 0,
  bottom: 0,
  [direction]: 0,
  display: 'flex',
  alignItems: 'center',
  zIndex: ugcZIndex + 1,
  backgroundColor: 'white',
  padding: '0 4px',
  opacity: visible ? 1 : 0,
  transition: 'opacity 0.2s ease',
  pointerEvents: visible ? 'auto' : 'none',
});

export default BuyBoxCarousel;
