import React from 'react'
import PropTypes from 'prop-types'

import { htmlAttrs } from '../utils/htmlAttrs'
import { selectSystemProps } from '../utils/props/selectSystemProps'
import { variantProp } from '../utils/props/variantProp'
import { viewProps } from '../utils/props/viewProps'
import { getTokensPropType } from '../utils/props/tokens'
import { OrderedListBase } from './OrderedListBase'

const [selectProps, selectedSystemPropsTypes] = selectSystemProps([htmlAttrs, viewProps])

export const OrderedList = React.forwardRef(
  ({ children, start = 1, variant = {}, tokens = {}, ...rest }, ref) => {
    // Pass any variants and tokens "OrderedList" receives down to the individual list items.
    const childrenWithParentVariants = React.useMemo(() => {
      const addVariantAndTokensToProps = (child, i, isLastChild) => {
        const existingChildVariants = child.props?.variant ?? {}
        const existingChildTokens = child.props?.tokens ?? {}

        return React.cloneElement(child, {
          index: start + i,
          isLastChild,
          variant: { ...existingChildVariants, ...variant },
          tokens: { ...existingChildTokens, ...tokens }
        })
      }

      if (variant || tokens) {
        const lastIndex = React.Children.count(children) - 1

        // `React.Children.map` and not a plain `.map`: `addVariantAndTokensToProps` clones each
        // child, and a clone is unvalidated, so returning it inside a plain array would make React
        // demand a key the caller cannot give for statically written items (#7254). It also handles
        // a single child, which is why the array branch is gone.
        return React.Children.map(children, (child, i) =>
          addVariantAndTokensToProps(child, i, i === lastIndex)
        )
      }

      return children
    }, [children, variant, start, tokens])

    return (
      <OrderedListBase ref={ref} {...selectProps(rest)}>
        {childrenWithParentVariants}
      </OrderedListBase>
    )
  }
)

OrderedList.propTypes = {
  ...selectedSystemPropsTypes,
  /**
   * A list of ordered items wrapped in `OrderedListItem`.
   */
  children: PropTypes.node.isRequired,
  /**
   * The position to start the list with.
   */
  start: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
  variant: variantProp.propType,
  tokens: getTokensPropType('OrderedList')
}

OrderedList.displayName = 'OrderedList'
