<script setup lang="ts">

import {twJoin, twMerge} from "tailwind-merge"

const uiConfig = {
  wrapper: 'relative',
  container: {
    base: 'group absolute',
    zIndex: 'z-[999]',
    placement: {
      top: '-top-1 left-1/2',
      bottom: '-bottom-1 left-1/2',
      left: '-left-1 top-1/2',
      right: '-right-1 top-1/2'
    }
  },
  width: 'max-w-xs',
  background: 'bg-deepblue-900 dark:bg-gray-50',
  backgroundError: 'bg-[#FAD9D7] dark:bg-[#4F393A]',
  color: 'text-white dark:text-deepblue-900',
  colorError: 'text-[#E74135] dark:text-[#F47E75]',
  rounded: 'rounded-md',
  base: 'text-xs font-normal absolute',
  padding: {
    sm: 'px-4 py-2',
    md: 'px-4 py-4'
  },
  placement: {
    top: {
      start: 'bottom-[1px] left-[-13px]',
      center: 'bottom-[1px] left-1/2 transform translate-x-[-50%]',
      end: 'bottom-[1px] right-[-13px]'
    },
    bottom: {
      start: 'top-[1px] left-[-13px]',
      center: 'top-[1px] left-1/2 transform translate-x-[-50%]',
      end: 'top-[1px] right-[-13px]'
    },
    left: {
      start: 'right-[1px] top-[-13px]',
      center: 'right-[1px] top-1/2 transform translate-y-[-50%]',
      end: 'right-[1px] bottom-[-13px]'
    },
    right: {
      start: 'left-[1px] top-[-13px]',
      center: 'left-[1px] top-1/2 transform translate-y-[-50%]',
      end: 'left-[1px] bottom-[-13px]'
    }
  },
  transition: {
    enterActiveClass: 'transition ease-out duration-200',
    enterFromClass: 'opacity-0 translate-y-1',
    enterToClass: 'opacity-100 translate-y-0',
    leaveActiveClass: 'transition ease-in duration-150',
    leaveFromClass: 'opacity-100 translate-y-0',
    leaveToClass: 'opacity-0 translate-y-1'
  },
  popper: {
    strategy: 'absolute',
    base: 'invisible before:visible before:block before:absolute before:rotate-45 before:z-19 before:w-3 before:h-3',
    rounded: 'before:rounded-sm',
    background: 'before:bg-deepblue-900 dark:before:bg-gray-50',
    backgroundError: 'before:bg-[#FAD9D7] dark:before:dark:bg-[#4F393A]',
    placement: {
      top: 'bottom-0 before:bottom-[-2px] before:left-[-6px]',
      bottom: 'top-0 before:top-[-2px] before:left-[-6px]',
      left: 'right-0 before:right-[-2px] before:top-[-6px]',
      right: 'left-0 before:left-[-2px] before:top-[-6px]'
    }
  },
}

interface Props {
  text?: string
  header?: string
  openDelay?: number
  closeDelay?: number
  prevent?: boolean
  placement?: 'top' | 'bottom' | 'left' | 'right'
  popper?: boolean
  popperPosition?: 'start' | 'center' | 'end'
  size?: 'sm' | 'md'
  zIndex?: number
  truncate?: boolean
  errorParsing?: boolean
  parent?: HTMLElement
}

const props = withDefaults(defineProps<Props>(), {
  text: '',
  header: '',
  openDelay: 0,
  closeDelay: 0,
  prevent: false,
  placement: 'top',
  popper: true,
  popperPosition: 'start',
  size: 'md',
  truncate: true,
  errorParsing: false
})

const slots = useSlots()
const body = ref<HTMLElement | null>(null)

const open = ref(false)
let openTimeout: NodeJS.Timeout | null = null
let closeTimeout: NodeJS.Timeout | null = null

const popperPosition = ref(props.popperPosition)

const wrapperClasses = uiConfig.wrapper
const containerClasses = computed(() => twMerge(twJoin(
  uiConfig.container.base,
  uiConfig.container.placement[props.placement],
  uiConfig.width,
  props.zIndex ? '' : uiConfig.container.zIndex
)))
const poperClasses = computed(() => twMerge(twJoin(
  uiConfig.popper.strategy,
  uiConfig.popper.base,
  uiConfig.popper.rounded,
  props.errorParsing ? uiConfig.popper.backgroundError : uiConfig.popper.background,
  uiConfig.popper.placement[props.placement]
)))
const baseClasses = computed(() => twMerge(twJoin(
  uiConfig.base,
  props.errorParsing ? uiConfig.backgroundError : uiConfig.background,
  props.errorParsing ? uiConfig.colorError : uiConfig.color,
  uiConfig.rounded,
  uiConfig.placement[props.placement][popperPosition.value],
  uiConfig.padding[props.size],
  props.truncate ? 'truncate' : 'max-w-[530px] w-max'
)))
const transitionClasses = uiConfig.transition

const containerStyles = computed(() => {
  return {
    zIndex: props.zIndex
  }
})


const hasSlot = (name: string): boolean => {
  return Boolean(slots[name])
}

const hasHeader = computed(() => hasSlot('header') || Boolean(props.header))

function onMouseEnter(e) {
  if (props.parent) {
    const parent = props.parent.getBoundingClientRect()
    const target = e.target

    setTimeout(() => {
      if (body.value) {
        let left = parent.width - (target.offsetLeft + (target.clientWidth / 2) + body.value.offsetWidth)

        if (left < 0) {
          popperPosition.value = 'end'
        }
      }
    })
  }
}

const onMouseOver = (e) => {
  if (closeTimeout) {
    clearTimeout(closeTimeout)
    closeTimeout = null
  }
  if (open.value) {
    return
  }

  openTimeout = openTimeout || setTimeout(() => {
    open.value = true
    openTimeout = null
  }, props.openDelay)
}

const onMouseLeave = () => {
  if (openTimeout) {
    clearTimeout(openTimeout)
    openTimeout = null
  }
  if (!open.value) {
    return
  }
  closeTimeout = closeTimeout || setTimeout(() => {
    open.value = false
    closeTimeout = null
  }, props.closeDelay)
}
</script>

<template>
  <div
    v-bind="$attrs"
    :class="wrapperClasses"
    @mouseover="onMouseOver"
    @mouseleave="onMouseLeave"
    @mouseenter="onMouseEnter"
  >
    <slot :open="open">
      Hover
    </slot>
    <Transition
      appear
      v-bind="transitionClasses"
    >
      <div
        v-if="open && !prevent"
        :class="containerClasses"
        :style="containerStyles"
      >
        <div
          v-if="popper"
          :class="poperClasses"
        />
        <div :class="baseClasses" ref="body">
          <div
            v-if="hasHeader"
            class="font-semibold mb-1"
          >
            <slot name="header">
              <p>{{ header }}</p>
            </slot>
          </div>

          <slot name="content">
            {{ text }}
          </slot>
        </div>
      </div>
    </Transition>
  </div>
</template>

<style scoped>

</style>
