import { ref, computed, watch, onUnmounted } from 'vue'
import type { Options, Placement } from '@popperjs/core'

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

export const useDropdownWithPopper = <T extends HTMLElement = never>(
  popperConfig?: Partial<Options>,
) => {
  // References to the dropdown elements
  const dropdownRefElement = ref<T | null>(null)
  const dropdownMenuElement = ref<HTMLDivElement | null>(null)

  // Reactive state to control dropdown visibility
  const isOpen = ref<boolean>(false)

  // Popper.js integration
  const { popper, initPopper, destroyPopper } = usePopper()

  // Computed Popper configuration based on RTL and user-provided settings
  const _popperConfig = computed(() => ({
    placement: (isRTL(dropdownRefElement.value) ? 'bottom-end' : 'bottom-start') as Placement,
    modifiers: [
      {
        name: 'preventOverflow',
        options: {
          boundary: 'clippingParents',
        },
      },
      {
        name: 'offset',
        options: {
          offset: [0, 2],
        },
      },
    ],
    ...popperConfig,
  }))

  // Methods to control dropdown state
  const closeDropdown = () => {
    isOpen.value = false
  }

  const openDropdown = () => {
    isOpen.value = true
  }

  const toggleDropdown = () => {
    isOpen.value = !isOpen.value
  }

  const updatePopper = () => {
    if (popper) {
      popper.update()
    }
  }

  // Event handlers
  const handleKeyUp = (event: KeyboardEvent) => {
    if (event.key === 'Escape') {
      isOpen.value = false
      return
    }

    if (event.key === 'Tab') {
      const activeElement = document.activeElement as HTMLElement

      if (
        dropdownRefElement.value?.contains(activeElement) ||
        dropdownMenuElement.value?.contains(activeElement)
      ) {
        return
      }

      isOpen.value = false
    }
  }

  const handleMouseUp = (event: Event) => {
    const target = event.target as HTMLElement

    if (dropdownMenuElement.value?.contains(target) || dropdownRefElement.value?.contains(target)) {
      return
    }

    isOpen.value = false
  }

  // Utility functions to manage event listeners
  const addEventListeners = () => {
    window.addEventListener('mouseup', handleMouseUp)
    window.addEventListener('keyup', handleKeyUp)
  }

  const removeEventListeners = () => {
    window.removeEventListener('mouseup', handleMouseUp)
    window.removeEventListener('keyup', handleKeyUp)
  }

  // Watcher to handle side effects when `isOpen` changes
  watch(isOpen, (open) => {
    if (open) {
      addEventListeners()

      if (dropdownRefElement.value && dropdownMenuElement.value) {
        initPopper(dropdownRefElement.value, dropdownMenuElement.value, _popperConfig.value)
      }
    } else {
      removeEventListeners()
      destroyPopper()
    }
  })

  // Cleanup on component unmount
  onUnmounted(() => {
    removeEventListeners()
    destroyPopper()
  })

  return {
    dropdownRefElement,
    dropdownMenuElement,
    isOpen,
    closeDropdown,
    openDropdown,
    toggleDropdown,
    updatePopper,
  }
}
