<template>
  <div
    ref="rootEl"
    :class="[cn.b(), cn.is('active', active), cn.is('focused', focused)]"
  >
    <UiRenderless ref="triggerEl">
      <slot v-bind="{ isActive: active }" />
    </UiRenderless>

    <Teleport
      to="body"
      :disabled="!teleport"
    >
      <div
        v-if="withOverlay"
        :class="[cn.e('overlay'), cn.is('active', active)]"
      />

      <Transition
        name="dropdown"
        @after-leave="scrollbarInstance()?.destroy()"
      >
        <div
          v-if="active"
          ref="popperEl"
          :class="[cn.e('popper')]"
          :style="floatingStyles"
        >
          <ul
            ref="scrollParentEl"
            role="listbox"
            :class="[cn.e('dropdown'), dropdownClass]"
          >
            <slot name="dropdown">
              <span>Content</span>
            </slot>
          </ul>
        </div>
      </Transition>
    </Teleport>
  </div>
</template>

<script setup lang="ts">
import { provide, ref, toRef, watch } from 'vue'
import {
  autoUpdate,
  flip as floatingFlip,
  offset as floatingOffset,
  useFloating,
} from '@floating-ui/vue'
import {
  onClickOutside,
  useElementHover,
  useEventListener,
} from '@vueuse/core'

import type { OffsetOptions, Placement } from '@floating-ui/vue'
import { useOverlayScrollbars } from 'overlayscrollbars-vue'
import useClassname from '../../composables/use-classname.js'
import UiRenderless from '../renderless/index.vue'
import { dropdownContextKey } from './constants'

export interface Props {
  trigger?: 'hover' | 'click'
  disabled?: false
  offset?: OffsetOptions
  placement?: Placement
  hideOnClick?: boolean
  withOverlay?: boolean
  dropdownClass?: string
  transitionName?: string
  teleport?: boolean
}

defineOptions({
  name: 'UiDropdown',
})

const props = withDefaults(defineProps<Props>(), {
  trigger: 'click',
  disabled: false,
  placement: 'bottom-start',
  offset: 4,
  hideOnClick: true,
  withOverlay: false,
  transitionName: 'ui-dropdown',
  teleport: false,
})

defineEmits<{
  'update:modelValue': []
  focus: []
  blur: []
}>()

const rootEl = ref<HTMLElement | null>()
const triggerEl = ref()
const popperEl = ref()
const scrollParentEl = ref()

const active = ref(false)
const focused = ref(false)

const cn = useClassname('ui-dropdown')

if (props.trigger === 'click') {
  useEventListener(triggerEl, 'click', () => {
    toggle()
  })

  onClickOutside(
    popperEl,
    () => {
      close()
    },
    { ignore: [triggerEl] },
  )
}
else {
  const isHoveredTrigger = useElementHover(triggerEl, { delayLeave: 300 })
  const isHoveredPopper = useElementHover(popperEl, { delayLeave: 10 })

  watch(isHoveredTrigger, (value: boolean) => {
    if (value)
      open()
    else if (!isHoveredPopper.value)
      close()
  })

  watch(isHoveredPopper, async (value: boolean) => {
    if (!value && !isHoveredTrigger.value)
      close()
  })
}

const { floatingStyles } = useFloating(triggerEl, popperEl, {
  placement: toRef(props, 'placement'),
  middleware: [floatingOffset(props.offset), floatingFlip()],
  whileElementsMounted: autoUpdate,
})

const [initScrollbar, scrollbarInstance] = useOverlayScrollbars({
  options: {
    scrollbars: {
      theme: 'os-small-theme',
    },
  },
})

function setActive(state: boolean) {
  if (props.disabled)
    return

  active.value = state
}

function toggle() {
  setActive(!active.value)
}

function open() {
  setActive(true)
}

function close() {
  setActive(false)
}

function handleItemClick() {
  if (props.hideOnClick)
    close()
}

// watch(scrollParentEl, (value) => {
//   if (value)
//     initScrollbar(value)
// })

provide(dropdownContextKey, {
  handleItemClick,
})
</script>

<style lang="scss">
@use 'styles';
</style>
