import { defineComponent, h, PropType, VNode } from 'vue'

import { CElementCover } from '../element-cover'
import { CVirtualScroller } from '../virtual-scroller'

import { getNextSibling, getPreviousSibling } from './utils'
import type { Option, OptionsGroup } from './types'

const CMultiSelectOptions = defineComponent({
  name: 'CMultiSelectOptions',
  props: {
    loading: Boolean,
    options: {
      type: Array as PropType<(Option | OptionsGroup)[]>,
      default: () => [],
    },
    optionsMaxHeight: {
      type: [Number, String],
      default: 'auto',
    },
    optionsStyle: {
      type: String,
      default: 'checkbox',
      validator: (value: string) => {
        return ['checkbox', 'text'].includes(value)
      },
    },
    scopedSlots: Object,
    searchNoResultsLabel: {
      type: String,
      default: 'no items',
    },
    selected: {
      type: Array as PropType<Option[]>,
      default: () => [],
    },
    virtualScroller: Boolean,
    visibleItems: {
      type: Number,
      default: 10,
    },
  },
  emits: ['optionClick'],
  setup(props, { emit }) {
    const handleKeyDown = (event: KeyboardEvent, option: Option) => {
      if (event.code === 'Space' || event.key === 'Enter') {
        event.preventDefault()
        handleOptionClick && handleOptionClick(option)
        return
      }

      if (event.key === 'Down' || event.key === 'ArrowDown') {
        event.preventDefault()
        const target = event.target as HTMLElement
        const next = getNextSibling(target, '.form-multi-select-option')

        next && (next as HTMLElement).focus()
      }

      if (event.key === 'Up' || event.key === 'ArrowUp') {
        event.preventDefault()
        const target = event.target as HTMLElement
        const prev = getPreviousSibling(target, '.form-multi-select-option')

        prev && (prev as HTMLElement).focus()
      }
    }

    const handleOptionClick = (option: Option) => {
      emit('optionClick', option as Option)
    }

    // TODO: find solution how to remove any
    const createOptions = (options: (Option | OptionsGroup)[]): VNode | VNode[] | any =>
      options.length > 0
        ? options.map((option: Option | OptionsGroup) =>
            'value' in option
              ? h(
                  'div',
                  {
                    class: [
                      'form-multi-select-option',
                      {
                        'form-multi-select-option-with-checkbox': props.optionsStyle === 'checkbox',
                        'form-multi-selected': props.selected.some(
                          (_option) => _option.value === option.value,
                        ),
                        disabled: option.disabled,
                      },
                    ],
                    onClick: () => handleOptionClick(option as Option),
                    onKeydown: (event: any) => handleKeyDown(event, option as Option),
                    tabindex: 0,
                  },
                  props.scopedSlots && props.scopedSlots['options']
                    ? h(props.scopedSlots['options'], { option: option })
                    : option.label,
                )
              : [
                  h(
                    'div',
                    { class: 'form-multi-select-optgroup-label' },
                    props.scopedSlots && props.scopedSlots['options-groups']
                      ? h(props.scopedSlots['options-groups'], { option: option })
                      : option.label,
                  ),
                ],
          )
        : h('div', { class: 'form-multi-select-options-empty' }, props.searchNoResultsLabel)

    return () => [
      props.virtualScroller
        ? h(
            CVirtualScroller,
            {
              class: 'form-multi-select-options',
              visibleItems: props.visibleItems,
            },
            {
              default: () => createOptions(props.options),
            },
          )
        : h(
            'div',
            {
              class: 'form-multi-select-options',
              ...(props.optionsMaxHeight !== 'auto' && {
                style: { maxHeight: props.optionsMaxHeight, overflow: 'scroll' },
              }),
            },
            createOptions(props.options),
          ),
      props.loading && h(CElementCover),
    ]
  },
})

export { CMultiSelectOptions }
