import React from 'react'
import PropTypes from 'prop-types'
import { View, Platform } from 'react-native'
import { layoutTags, getA11yPropsFromHtmlTag } from '../../utils/a11y/semantics'
import { selectSystemProps } from '../../utils/props/selectSystemProps'
import { a11yProps } from '../../utils/props/a11yProps'
import { viewProps } from '../../utils/props/viewProps'
import { variantProp } from '../../utils/props/variantProp'
import { useCarousel } from '../CarouselContext'
import { ITEMS_PER_VIEWPORT_MD, ITEMS_PER_VIEWPORT_LG_XL, GAP_BETWEEN_ITEMS } from '../Constants'

const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps])

const selectContainerStyle = ({
  width,
  elementIndex,
  enablePeeking,
  peekingGap,
  hidden,
  enableDisplayMultipleItemsPerSlide,
  viewport,
  peekingMiddleSpace,
  maxWidth,
  viewportWidth
}) => {
  let adjustedWidth = width
  let marginLeft = 0

  if (enablePeeking) {
    const isFirst = elementIndex === 0
    const clampedMaxWidth = Math.min(maxWidth || width, width)
    adjustedWidth = clampedMaxWidth - peekingMiddleSpace * 2
    if (isFirst) {
      marginLeft = peekingMiddleSpace + (viewportWidth - clampedMaxWidth) / 2
    } else {
      marginLeft = peekingGap
    }
  }

  if (enableDisplayMultipleItemsPerSlide) {
    switch (viewport) {
      case 'xs':
      case 'sm':
        break
      case 'md':
        if (enablePeeking) {
          adjustedWidth =
            adjustedWidth / ITEMS_PER_VIEWPORT_MD - (peekingGap * 1) / ITEMS_PER_VIEWPORT_MD
        } else {
          adjustedWidth =
            width / ITEMS_PER_VIEWPORT_MD - (GAP_BETWEEN_ITEMS * 2) / ITEMS_PER_VIEWPORT_MD
          marginLeft = GAP_BETWEEN_ITEMS
        }
        break
      case 'lg':
      case 'xl':
        if (enablePeeking) {
          adjustedWidth =
            adjustedWidth / ITEMS_PER_VIEWPORT_LG_XL - (peekingGap * 2) / ITEMS_PER_VIEWPORT_LG_XL
        } else {
          adjustedWidth =
            width / ITEMS_PER_VIEWPORT_LG_XL - (GAP_BETWEEN_ITEMS * 3) / ITEMS_PER_VIEWPORT_LG_XL
          marginLeft = GAP_BETWEEN_ITEMS
        }
        break
      default:
        break
    }
  }

  const style = {
    width: adjustedWidth,
    marginLeft
  }

  if (hidden && Platform.OS === 'web') {
    style.visibility = 'hidden'
  }

  return style
}

/**
 * `CarouselItem` is used to wrap the content of an individual slide and is suppsoed to be the
 * only top-level component passed to the `Carousel`
 */
export const CarouselItem = React.forwardRef(
  (
    {
      children,
      elementIndex,
      tag = 'li',
      hidden,
      enablePeeking,
      peekingProps,
      enableDisplayMultipleItemsPerSlide,
      viewport,
      backgroundColor,
      ...rest
    },
    ref
  ) => {
    const { width, activeIndex, goTo, maximumItemsForSlide, maxWidth, viewportWidth } =
      useCarousel()

    const selectedProps = selectProps({
      ...rest,
      ...getA11yPropsFromHtmlTag(tag, rest.accessibilityRole)
    })

    const focusabilityProps =
      activeIndex === elementIndex || enablePeeking ? {} : a11yProps.nonFocusableProps

    const handleFocus = React.useCallback(
      (event) => {
        if (Platform.OS === 'web') {
          if (enablePeeking) {
            if (enableDisplayMultipleItemsPerSlide) {
              const startIndex = maximumItemsForSlide * activeIndex
              const endIndex = startIndex + maximumItemsForSlide - 1
              if (elementIndex < startIndex) {
                if (activeIndex - 1 < 0) {
                  goTo(0)
                } else {
                  goTo(activeIndex - 1)
                }
              } else if (elementIndex > endIndex) {
                goTo(activeIndex + 1)
              }
            } else if (elementIndex !== activeIndex) {
              if (elementIndex > activeIndex) {
                goTo(activeIndex + 1)
              } else if (elementIndex < activeIndex) {
                goTo(activeIndex - 1)
              }
            }
          }
        }

        if (rest.onFocus) {
          rest.onFocus(event)
        }
      },
      [
        elementIndex,
        activeIndex,
        goTo,
        maximumItemsForSlide,
        rest,
        enablePeeking,
        enableDisplayMultipleItemsPerSlide
      ]
    )

    return (
      <View
        style={selectContainerStyle({
          width,
          elementIndex,
          hidden,
          enablePeeking,
          enableDisplayMultipleItemsPerSlide,
          viewport,
          maxWidth,
          viewportWidth,
          ...peekingProps
        })}
        {...selectedProps}
        {...focusabilityProps}
        ref={ref}
        onFocus={handleFocus}
      >
        {children}
      </View>
    )
  }
)

CarouselItem.propTypes = {
  ...selectedSystemPropTypes,
  variant: variantProp.propType,
  /**
   * Index of the current slide
   * Don't pass this prop when using `CarouselItem` as it is already being passed by `Carousel` top-level component
   */
  elementIndex: PropTypes.number,
  /**
   * Provide custom accessibilityLabelledBy for Carousel slide
   */
  accessibilityLabelledBy: PropTypes.string,
  /**
   * Content of the slide
   */
  children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]).isRequired,
  /**
   * Sets the HTML tag of the outer container. By default `'li'` so that assistive technology sees
   * the Carousel as a list of items.
   *
   * Carousel's innermost container defaults to `'ul'` which can be overridden. If the tag of either
   * `Carousel` or `CarouselItem` is overriden, the other should be too, to avoid producing invalid HTML.
   */
  tag: PropTypes.oneOf(layoutTags),
  /**
   * Function to set carousel content background color when slide is being display
   */
  setContentBackgroundColor: PropTypes.func,
  /**
   * Boolean to enable peeking effect on the first and last slide
   */
  enablePeeking: PropTypes.bool,
  /**
   * Object to configure the peeking effect
   */
  peekingProps: PropTypes.shape({
    peekingMarginLeft: PropTypes.number,
    peekingMarginRight: PropTypes.number,
    peekingFirstSpace: PropTypes.number,
    peekingLastSpace: PropTypes.number,
    peekingMiddleSpace: PropTypes.number,
    peekingGap: PropTypes.number
  }),
  /**
   * Background color of the slide. It supports Box background variant colors.
   */
  backgroundColor: PropTypes.string
}

CarouselItem.displayName = 'CarouselItem'
