import {
  Children,
  forwardRef,
  Fragment,
  isValidElement,
  useCallback,
  useMemo,
  useRef,
  useState,
} from 'react'

import {useClickOutsideEvent} from '../../hooks'
import {Box, Popover, Stack, Text} from '../../primitives'
import {_getArrayProp} from '../../styles'
import {ExpandButton, StyledBreadcrumbs} from './breadcrumbs.styles'

/**
 * @beta
 */
export interface BreadcrumbsProps {
  maxLength?: number
  separator?: React.ReactNode
  gap?: number | number[]
  /**
   * @deprecated Use `gap` instead. `space` will be removed in v4.
   */
  space?: number | number[]
}

/**
 * @beta
 */
export const Breadcrumbs = forwardRef(function Breadcrumbs(
  props: BreadcrumbsProps & Omit<React.HTMLProps<HTMLOListElement>, 'as' | 'ref' | 'type'>,
  ref: React.ForwardedRef<HTMLOListElement>,
) {
  const {children, gap, maxLength, separator, space: deprecated_space = 2, ...restProps} = props
  const space = _getArrayProp(gap === undefined ? deprecated_space : gap)
  const [open, setOpen] = useState(false)
  const expandElementRef = useRef<HTMLButtonElement | null>(null)
  const popoverElementRef = useRef<HTMLDivElement | null>(null)

  const collapse = useCallback(() => setOpen(false), [])
  const expand = useCallback(() => setOpen(true), [])

  useClickOutsideEvent(collapse, () => [expandElementRef.current, popoverElementRef.current])

  const rawItems = useMemo(() => Children.toArray(children).filter(isValidElement), [children])

  const items = useItems({
    collapse,
    expand,
    expandElementRef,
    maxLength,
    open,
    popoverElementRef,
    rawItems,
    space,
  })

  return (
    <StyledBreadcrumbs data-ui="Breadcrumbs" {...restProps} ref={ref}>
      {items.map((item, itemIndex) => (
        <Fragment key={itemIndex}>
          {itemIndex > 0 && (
            <Box aria-hidden as="li" paddingX={space}>
              {separator || <Text muted>/</Text>}
            </Box>
          )}
          <Box as="li">{item}</Box>
        </Fragment>
      ))}
    </StyledBreadcrumbs>
  )
})
Breadcrumbs.displayName = 'ForwardRef(Breadcrumbs)'

function useItems({
  collapse,
  expand,
  expandElementRef,
  maxLength,
  open,
  popoverElementRef,
  rawItems,
  space,
}: {
  collapse: () => void
  expand: () => void
  expandElementRef: React.RefObject<HTMLButtonElement | null>
  maxLength: number | undefined
  open: boolean
  popoverElementRef: React.RefObject<HTMLDivElement | null>
  rawItems: React.ReactElement<unknown, string | React.JSXElementConstructor<any>>[]
  space: number[]
}) {
  const len = rawItems.length

  if (maxLength && len > maxLength) {
    const beforeLength = Math.ceil(maxLength / 2)
    const afterLength = Math.floor(maxLength / 2)

    return [
      ...rawItems.slice(0, beforeLength - 1),
      <Popover
        constrainSize
        content={
          <Stack as="ol" overflow="auto" padding={space} gap={space}>
            {rawItems.slice(beforeLength - 1, len - afterLength)}
          </Stack>
        }
        key="button"
        open={open}
        placement="top"
        portal
        ref={popoverElementRef}
      >
        <ExpandButton
          fontSize={1}
          mode="bleed"
          onClick={open ? collapse : expand}
          padding={1}
          ref={expandElementRef}
          selected={open}
          text="…"
        />
      </Popover>,
      ...rawItems.slice(len - afterLength),
    ]
  }

  return rawItems
}
