import React, { forwardRef, HTMLAttributes, useContext, useEffect, useState, useRef } from 'react'
import PropTypes from 'prop-types'
import classNames from 'classnames'

import { useForkedRef } from '../../hooks'

import { CCarouselContext } from './CCarouselContext'

export interface CCarouselItemProps extends HTMLAttributes<HTMLDivElement> {
  /**
   * @ignore
   */
  active?: boolean
  /**
   * A string of all className you want applied to the base component.
   */
  className?: string
  /**
   * @ignore
   */
  direction?: string
  /**
   * The amount of time to delay between automatically cycling an item.
   */
  interval?: boolean | number
}

export const CCarouselItem = forwardRef<HTMLDivElement, CCarouselItemProps>(
  ({ children, className, active, direction, interval = false, ...rest }, ref) => {
    const { setAnimating, setCustomInterval } = useContext(CCarouselContext)
    const carouselItemRef = useRef<HTMLDivElement>(null)
    const forkedRef = useForkedRef(ref, carouselItemRef)

    const prevActive = useRef<boolean>(undefined)
    const [directionClassName, setDirectionClassName] = useState<string>()
    const [orderClassName, setOrderClassName] = useState<string>()
    const [activeClassName, setActiveClassName] = useState(active && 'active')
    const [count, setCount] = useState(0)

    useEffect(() => {
      if (active) {
        setCustomInterval(interval)
        if (count !== 0) setOrderClassName(`carousel-item-${direction}`)
      }

      if (prevActive.current && !active) {
        setActiveClassName('active')
      }

      if (active || prevActive.current) {
        setTimeout(() => {
          if (count !== 0) {
            // @ts-expect-error reflow is necessary to proper transition
            // eslint-disable-next-line @typescript-eslint/no-unused-vars
            const reflow = carouselItemRef.current?.offsetHeight
            setDirectionClassName(`carousel-item-${direction === 'next' ? 'start' : 'end'}`)
          }
        }, 0)
      }

      prevActive.current = active

      if (count === 0) setCount(count + 1)
    }, [active])

    useEffect(() => {
      const element = carouselItemRef.current

      if (!element) {
        return
      }

      const handleTransitionStart = () => {
        if (active) {
          setAnimating(true)
        }
      }

      const handleTransitionEnd = () => {
        if (active) {
          setAnimating(false)
        }

        setDirectionClassName('')
        setOrderClassName('')
        setActiveClassName(active ? 'active' : '')
      }

      element.addEventListener('transitionstart', handleTransitionStart)
      element.addEventListener('transitionend', handleTransitionEnd)

      return () => {
        element.removeEventListener('transitionstart', handleTransitionStart)
        element.removeEventListener('transitionend', handleTransitionEnd)
      }
    }, [active])

    return (
      <div
        className={classNames(
          'carousel-item',
          activeClassName,
          directionClassName,
          orderClassName,
          className
        )}
        ref={forkedRef}
        {...rest}
      >
        {children}
      </div>
    )
  }
)

CCarouselItem.propTypes = {
  active: PropTypes.bool,
  children: PropTypes.node,
  className: PropTypes.string,
  direction: PropTypes.string,
  interval: PropTypes.oneOfType([PropTypes.bool, PropTypes.number]),
}

CCarouselItem.displayName = 'CCarouselItem'
