import { cloneVNode, defineComponent, h, PropType, ref, watch } from 'vue'
import { createPopper } from '@popperjs/core'

import { CConditionalTeleport } from '../conditional-teleport'

import { isRTL } from '../../utils'

const CPicker = defineComponent({
  name: 'CPicker',
  props: {
    /**
     * Set container type for the component.
     */
    container: {
      type: String as PropType<'dropdown' | 'inline'>,
      default: 'dropdown',
    },
    /**
     * Toggle the disabled state for the component.
     */
    disabled: Boolean,
    /**
     * A string of all className you want applied to the dropdown menu.
     */
    dropdownClassNames: String,
    /**
     * Toggle visibility of footer element or set the content of footer.
     */
    footer: Boolean,
    /**
     * Generates dropdown menu using Teleport.
     *
     * @since 5.8.0
     */
    teleport: {
      type: [Boolean],
      default: false,
    },
    /**
     * Toggle the visibility of the component.
     */
    visible: Boolean,
  },
  emits: [
    /**
     * Callback fired when the component requests to be hidden.
     */
    'hide',
    /**
     * Callback fired when the component requests to be shown.
     */
    'show',
  ],
  setup(props, { attrs, emit, slots }) {
    const pickerRef = ref()
    const dropdownRef = ref()
    const togglerRef = ref()

    const popper = ref()
    const visible = ref(props.visible)

    watch(
      () => props.visible,
      () => {
        visible.value = props.visible
      },
    )

    watch(visible, () => {
      if (props.container === 'inline') {
        return
      }

      if (visible.value) {
        emit('show')
        window.addEventListener('mouseup', handleMouseUp)
        window.addEventListener('keyup', handleKeyUp)
        initPopper()

        return
      }

      emit('hide')
      window.removeEventListener('mouseup', handleMouseUp)
      window.removeEventListener('keyup', handleKeyUp)

      destroyPopper()
    })

    const initPopper = () => {
      if (togglerRef.value && dropdownRef.value) {
        popper.value = createPopper(togglerRef.value, dropdownRef.value, {
          placement: isRTL(pickerRef.value) ? 'bottom-end' : 'bottom-start',
          modifiers: [
            {
              name: 'preventOverflow',
              options: {
                boundary: 'clippingParents',
              },
            },
            {
              name: 'offset',
              options: {
                offset: [0, 2],
              },
            },
          ],
        })
      }
    }

    const destroyPopper = () => {
      if (popper.value) {
        popper.value.destroy()
      }

      popper.value = undefined
    }

    const handleKeyUp = (event: KeyboardEvent) => {
      if (event.key === 'Escape') {
        visible.value = false
      }
    }

    const handleMouseUp = (event: Event) => {
      if (pickerRef.value && pickerRef.value.contains(event.target as HTMLElement)) {
        return
      }

      if (dropdownRef.value && dropdownRef.value.contains(event.target as HTMLElement)) {
        return
      }

      visible.value = false
    }

    switch (props.container) {
      case 'inline': {
        return () => h('div', { class: 'picker', ref: pickerRef }, slots.default && slots.default())
      }
      default: {
        return () =>
          h(
            'div',
            {
              class: [
                attrs.class,
                {
                  show: visible.value,
                },
              ],
              ref: pickerRef,
            },
            [
              /**
               * @slot Location for the toggler element.
               */
              slots.toggler &&
                slots.toggler().map((slot) =>
                  cloneVNode(slot, {
                    onClick: () => {
                      if (!props.disabled && !visible.value) {
                        visible.value = true
                      }
                    },
                    ref: (el) => {
                      togglerRef.value = el
                    },
                  }),
                ),
              h(
                CConditionalTeleport,
                {
                  teleport: props.teleport,
                },
                {
                  default: () =>
                    h(
                      'div',
                      {
                        class: [
                          props.dropdownClassNames,
                          {
                            show: props.teleport && visible.value,
                          },
                        ],
                        ref: dropdownRef,
                      },
                      [
                        slots.default && slots.default(),
                        /**
                         * @slot Location for the footer element.
                         */
                        props.footer && slots.footer && slots.footer(),
                      ],
                    ),
                },
              ),
            ],
          )
      }
    }
  },
})
export { CPicker }
