import { defineComponent, h, onUpdated, PropType, ref, watch } from 'vue'

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

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

const CTimePickerRollCol = defineComponent({
  name: 'CTimePickerRollCol',
  props: {
    ariaLabel: String,
    columnIndex: Number,
    columnRefs: {
      type: Array as PropType<(HTMLElement | null)[]>,
      default: () => [],
    },
    elements: {
      type: Array as PropType<Element[]>,
      required: true,
    },
    selected: [Number, String, null],
    setColumnRef: Function as PropType<(index: number, el: HTMLElement | null) => void>,
  },
  emits: ['click'],
  setup(props, { emit }) {
    const init = ref(true)
    const colRef = ref<HTMLElement>()
    const isVisible = useIsVisible(colRef)

    const scrollToSelectedElement = () => {
      const nodeEl = colRef.value?.querySelector('.selected')
      if (isVisible.value && nodeEl && nodeEl instanceof HTMLElement) {
        colRef.value?.scrollTo({
          top: nodeEl.offsetTop,
          behavior: init.value ? 'instant' : 'smooth',
        })
      }
    }

    watch(isVisible, () => {
      scrollToSelectedElement()

      if (isVisible.value) {
        init.value = false
      }
    })

    watch(
      colRef,
      () => {
        if (props.columnIndex !== undefined && props.setColumnRef) {
          props.setColumnRef(props.columnIndex, colRef.value || null)
        }
      },
      { immediate: true }
    )

    onUpdated(() => {
      scrollToSelectedElement()
    })

    const moveFocusToNextColumn = () => {
      if (!props.columnRefs || props.columnIndex === undefined) return

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

    const moveFocusToPreviousColumn = () => {
      if (!props.columnRefs || props.columnIndex === undefined) return

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

    const handleKeyDown = (event: KeyboardEvent, value: number | string) => {
      if (event.code === 'Space' || event.key === 'Enter') {
        event.preventDefault()
        emit('click', value)
        moveFocusToNextColumn()
        return
      }

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

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

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

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

    return () =>
      h(
        'div',
        {
          class: 'time-picker-roll-col',
          onFocusout: (event: FocusEvent) => {
            const currentTarget = event.currentTarget as HTMLElement
            const relatedTarget = event.relatedTarget as HTMLElement | null
            if (currentTarget && (!relatedTarget || !currentTarget.contains(relatedTarget))) {
              scrollToSelectedElement()
            }
          },
          ref: colRef,
          role: 'listbox',
          'aria-label': props.ariaLabel,
        },
        props.elements.map((element, index) => {
          const isSelected = element.value === props.selected
          return h(
            'div',
            {
              class: [
                'time-picker-roll-cell',
                {
                  selected: isSelected,
                },
              ],
              onClick: () => emit('click', element.value),
              onKeydown: (event: KeyboardEvent) => handleKeyDown(event, element.value),
              role: 'option',
              tabindex: isSelected ? 0 : props.selected ? -1 : index === 0 ? 0 : -1,
              'aria-label': element.label.toString(),
              'aria-selected': isSelected,
            },
            element.label
          )
        })
      )
  },
})
export { CTimePickerRollCol }
