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

import { useForkedRef, useIsVisible } from '../../hooks'
import { getNextActiveElement, isRTL } from '../../utils'

export interface Element {
  value: number | string
  label: number | string
}

export interface CTimePickerRollColProps {
  ariaLabel?: string
  columnIndex: number
  columnRefs: (HTMLDivElement | null)[]
  elements: Element[]
  onClick?: (value: number | string) => void
  selected?: number | string | null
}

export const CTimePickerRollCol = forwardRef<HTMLDivElement, CTimePickerRollColProps>(
  ({ ariaLabel, columnIndex, columnRefs, elements, onClick, selected }, ref) => {
    const colRef = useRef<HTMLDivElement>(null)
    const firstRender = useRef(true)
    const forkedRef = useForkedRef(ref, colRef)
    const isVisible = useIsVisible(colRef)

    useEffect(() => {
      if (isVisible) {
        setRoll(firstRender.current ? 'instant' : 'smooth')

        firstRender.current = false
      }
    }, [isVisible, selected])

    const moveFocusToNextColumn = useCallback(() => {
      const columns = columnRefs
      if (columns?.length === 0) {
        return
      }

      if (columnIndex < columns.length - 1) {
        const column = columns[columnIndex + 1]
        ;(column?.querySelector('.time-picker-roll-cell[tabindex="0"') as HTMLElement).focus()
      }
    }, [columnIndex, columnRefs])

    const moveFocusToPreviousColumn = useCallback(() => {
      const columns = columnRefs
      if (columns?.length === 0) {
        return
      }

      if (columnIndex > 0) {
        const column = columns[columnIndex - 1]
        ;(column?.querySelector('.time-picker-roll-cell[tabindex="0"') as HTMLElement).focus()
      }
    }, [columnIndex, columnRefs])

    const setRoll = useCallback((behavior: 'instant' | 'smooth') => {
      const nodeEl = colRef.current?.querySelector('.selected')
      if (nodeEl && nodeEl instanceof HTMLElement) {
        colRef.current?.scrollTo({
          top: nodeEl.offsetTop,
          behavior: behavior,
        })
      }
    }, [])

    const handleKeyDown = useCallback(
      (event: React.KeyboardEvent<HTMLDivElement>, value: number | string) => {
        if (event.code === 'Space' || event.key === 'Enter') {
          event.preventDefault()
          onClick?.(value)
          moveFocusToNextColumn()
          return
        }

        if (colRef.current && (event.key === 'ArrowDown' || event.key === 'ArrowUp')) {
          event.preventDefault()
          const target = event.target as HTMLElement
          const items = [
            ...colRef.current.querySelectorAll('.time-picker-roll-cell'),
          ] as HTMLElement[]

          getNextActiveElement(items, target, event.key === 'ArrowDown', true).focus()
          return
        }

        if (colRef.current && (event.key === 'Home' || event.key === 'End')) {
          event.preventDefault()
          const items = [
            ...colRef.current.querySelectorAll('.time-picker-roll-cell'),
          ] as HTMLElement[]
          const index = event.key === 'Home' ? 0 : items.length - 1
          items[index].focus()
          return
        }

        if (colRef.current && (event.key === 'ArrowLeft' || event.key === 'ArrowRight')) {
          event.preventDefault()
          const rtl = isRTL(colRef.current)
          const shouldGoLeft =
            (event.key === 'ArrowLeft' && !rtl) || (event.key === 'ArrowRight' && rtl)
          if (shouldGoLeft) {
            moveFocusToPreviousColumn()
          } else {
            moveFocusToNextColumn()
          }
        }
      },
      [onClick, moveFocusToNextColumn, moveFocusToPreviousColumn]
    )

    return (
      <div
        className="time-picker-roll-col"
        onBlur={(event) => {
          if (!event.currentTarget.contains(event.relatedTarget)) {
            setRoll('smooth')
          }
        }}
        role="listbox"
        aria-label={ariaLabel}
        ref={forkedRef}
      >
        {elements.map((element, index) => {
          const isSelected = element.value === selected
          return (
            <div
              className={classNames('time-picker-roll-cell', {
                selected: isSelected,
              })}
              key={index}
              onClick={() => onClick?.(element.value)}
              onKeyDown={(event) => handleKeyDown(event, element.value)}
              role="option"
              tabIndex={isSelected ? 0 : selected ? -1 : index === 0 ? 0 : -1}
              aria-label={element.label.toString()}
              aria-selected={isSelected}
            >
              {element.label}
            </div>
          )
        })}
      </div>
    )
  }
)

CTimePickerRollCol.propTypes = {
  elements: PropTypes.array.isRequired,
  onClick: PropTypes.func,
  selected: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
}

CTimePickerRollCol.displayName = 'CTimePickerRollCol'
