import { useEffect, useMemo, useRef, useState } from 'react'

import { styled, useStyleConfig, vui, VuiComponent } from '../core'
import { cs, filterUndefined, isArray, isReactText } from '../utils'
import { ListProvider } from './context'
import { ListItemProps, ListProps } from './list.types'
import ListDivider from './listDivider'
import ListHeading from './listHeading'
import ListIcon from './listIcon'
import ListItem from './listItem'
import ListText from './listText'

const clickKeys = ['Enter', ' ']
const nextKeys = ['ArrowDown', 'Tab']
const prevKeys = ['ArrowUp']

export const ListBase = styled.ulBox``

/** Displays a list of list link items and optional heading. Exposes some props to the children via context. */
export const List = vui<'ul', ListProps>((props, ref) => {
  const { children, className, heading, isInteractive, items, size, variant, ...rest } = props
  const styles = useStyleConfig('List', props)

  const context = useMemo(() => filterUndefined({ isInteractive, size, variant }), [isInteractive, size, variant])

  const listRef = useRef<HTMLUListElement>(null)

  const [_, setActive] = useState(-1)

  const onKeydown = (event: KeyboardEvent) => {
    if ([...prevKeys, ...nextKeys, ...clickKeys].includes(event.key)) {
      event.preventDefault()
      setActive((a: number) => {
        let active = a
        if (listRef.current) {
          const allChildren = Array.from(listRef.current.childNodes)
          const tabIndexedChildren = allChildren.filter(child => (child as HTMLElement).tabIndex === 0)

          if (nextKeys.includes(event.key) && active < tabIndexedChildren.length - 1) active++
          if (prevKeys.includes(event.key)) active = active > 0 ? active - 1 : 0

          tabIndexedChildren.forEach((node, index) => {
            if (index === active) {
              if (clickKeys.includes(event.key)) (node as HTMLElement)?.click()
              else (node as HTMLElement)?.focus()
            }
          })
        }
        return active
      })
    }
  }

  useEffect(() => {
    if (listRef.current && isInteractive) {
      listRef.current.addEventListener('keydown', onKeydown)
      return () => {
        listRef.current?.removeEventListener('keydown', onKeydown)
      }
    }
  }, [listRef.current, isInteractive])

  return (
    <ListProvider value={context}>
      <ListBase className={cs('vui-list', className)} ref={ref || listRef} {...styles.container} {...rest}>
        {isReactText(heading) ? <ListHeading text={heading} /> : heading}
        {children ??
          (isArray<ListItemProps>(items)
            ? items.map(({ key, ...props }, index) => <ListItem key={key ?? index} {...props} />)
            : items)}
      </ListBase>
    </ListProvider>
  )
}) as VuiComponent<'ul', ListProps> & {
  Divider: typeof ListDivider
  Heading: typeof ListHeading
  Icon: typeof ListIcon
  Item: typeof ListItem
  Text: typeof ListText
}

List.Divider = ListDivider
List.Heading = ListHeading
List.Icon = ListIcon
List.Item = ListItem
List.Text = ListText
List.displayName = 'List'
export default List
