import { useEffect, useRef, useState } from 'react'
import { type Options, type Placement } from '@popperjs/core'

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

export const useDropdownWithPopper = <T extends HTMLElement = never>(
  popperConfig?: Partial<Options>,
) => {
  const dropdownRefElement = useRef<T>(null)
  const dropdownMenuElement = useRef<HTMLDivElement | null>(null)
  const [isOpen, setIsOpen] = useState<boolean>(false)

  const { popper, initPopper, destroyPopper } = usePopper()

  const _popperConfig = {
    placement: (isRTL(dropdownRefElement.current) ? 'bottom-end' : 'bottom-start') as Placement,
    modifiers: [
      {
        name: 'preventOverflow',
        options: {
          boundary: 'clippingParents',
        },
      },
      {
        name: 'offset',
        options: {
          offset: [0, 2],
        },
      },
    ],
    ...popperConfig,
  }

  const closeDropdown = () => {
    setIsOpen(false)
  }

  const openDropdown = () => {
    setIsOpen(true)
  }

  const toggleDropdown = () => {
    setIsOpen((prev) => !prev)
  }

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

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

    if (event.key === 'Tab') {
      if (
        dropdownRefElement.current &&
        dropdownRefElement.current.contains(document.activeElement as HTMLElement)
      ) {
        return
      }

      if (
        dropdownMenuElement.current &&
        dropdownMenuElement.current.contains(document.activeElement as HTMLElement)
      ) {
        return
      }

      setIsOpen(false)
    }
  }

  const handleMouseUp = (event: Event) => {
    if (
      (dropdownMenuElement.current &&
        dropdownMenuElement.current.contains(event.target as HTMLElement)) ||
      (dropdownRefElement.current &&
        dropdownRefElement.current.contains(event.target as HTMLElement))
    ) {
      return
    }

    setIsOpen(false)
  }

  useEffect(() => {
    if (isOpen) {
      window.addEventListener('mouseup', handleMouseUp)
      window.addEventListener('keyup', handleKeyUp)

      if (dropdownRefElement.current && dropdownMenuElement.current) {
        initPopper(dropdownRefElement.current, dropdownMenuElement.current, _popperConfig)
      }
    } else {
      window.removeEventListener('mouseup', handleMouseUp)
      window.removeEventListener('keyup', handleKeyUp)

      destroyPopper()
    }

    return () => {
      window.removeEventListener('mouseup', handleMouseUp)
      window.removeEventListener('keyup', handleKeyUp)

      destroyPopper()
    }
  }, [isOpen])

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