import { makeComponentProps } from '@/composables/component'
import { makeTagProps } from '@/composables/tag'
import { genericComponent, propsFactory, useRender } from '@/utils'
import { ExtractPropTypes } from 'vue'
import { computed, ref, watch, onMounted } from 'vue'
import { createPopper, Instance, Placement } from '@popperjs/core'

export const makeUTooltipProps = propsFactory(
  {
    theme: {
      type: String,
      default: 'dark',
      required: false,
    },
    position: {
      type: String,
      default: 'none bottom',
      required: false,
    },
    always: {
      type: Boolean,
      default: false,
      required: false,
    },

    ...makeComponentProps(),
    ...makeTagProps(),
  },
  'UTooltip'
)

export type UTooltipProps = ExtractPropTypes<typeof makeUTooltipProps>

export type UTooltipSlots = {
  tooltipToggle: never
  text: never
  supportingText: never
}

export const UTooltip = genericComponent<UTooltipSlots>()({
  name: 'UTooltip',

  props: makeUTooltipProps(),

  emits: {
    click: (e: MouseEvent) => true,
  },

  setup(props, { emit, slots }) {
    const tooltipIsVisible = ref(false)
    const tooltipToggle = ref<HTMLElement | Element | null>(null)
    const tooltipComponent = ref<Element | HTMLElement | null>(null)
    const popperInstance = ref<Instance | null>(null)
    const alwaysVisible = computed(() => props.always)
    const popperCreated = ref(false)

    watch(
      () => props.position,
      () => {
        destroyTooltip()
        props.always === true ? createTooltip() : ''
      }
    )

    watch(alwaysVisible, (newValue) => {
      if (newValue === true) {
        destroyTooltip()
        createTooltip()
      } else {
        destroyTooltip()
      }
    })

    function createTooltip() {
      if (tooltipToggle.value && tooltipComponent.value) {
        tooltipIsVisible.value = true
        tooltipComponent.value.removeAttribute('data-popper-reference-hidden')
        let placement = props.position as Placement
        let offset = [0, 12]

        if (props.position === 'none top' || props.position === 'none bottom') {
          offset = [0, 6]
        } else if (
          props.position === 'top-end' ||
          props.position === 'bottom-end'
        ) {
          offset = [13, 12]
        } else if (
          props.position === 'top-start' ||
          props.position === 'bottom-start'
        ) {
          offset = [-14, 12]
        }

        if (props.position === 'none top') {
          placement = 'top'
        } else if (props.position === 'none bottom') {
          placement = 'bottom'
        }

        setTimeout(() => {
          popperInstance.value = createPopper(
            tooltipToggle.value as HTMLElement,
            tooltipComponent.value as HTMLElement,
            {
              placement: placement,
              modifiers: [
                {
                  name: 'offset',
                  options: {
                    offset: offset,
                  },
                },
              ],
            }
          )
          popperCreated.value = true
        }, 5)
      }
    }

    function destroyTooltip() {
      tooltipIsVisible.value = false
      popperCreated.value = false

      if (popperInstance.value) {
        popperInstance.value.destroy()
        popperInstance.value = null
      }
    }

    onMounted(() => {
      if (alwaysVisible.value === true) {
        createTooltip()
      }
    })

    const tooltipTheme = computed(() => {
      let tooltipTheme = {
        color: 'white',
        backgroundColor: 'gray-900',
      }
      switch (props.theme) {
        case 'dark':
          tooltipTheme = {
            color: 'white',
            backgroundColor: 'gray-900',
          }
          break
        case 'light':
          tooltipTheme = {
            color: 'gray-700',
            backgroundColor: 'white',
          }
          break
        default:
          break
      }
      return tooltipTheme
    })

    const GeneralClasses = `absolute flex flex-col font-medium max-w-sm py-2 px-3 text-text-xs
      rounded-lg w-fit`

    const ThemeDarkClasses = computed(
      () =>
        `bg-${tooltipTheme.value.backgroundColor} text-${tooltipTheme.value.color} shadow
          shadow-lg shadow-${tooltipTheme.value.backgroundColor}`
    )

    const ThemeLightClasses = computed(
      () =>
        `bg-${tooltipTheme.value.backgroundColor} text-${tooltipTheme.value.color} shadow
          shadow-lg shadow-gray-100`
    )

    const PseudoElementClasses = computed(
      () =>
        `before:block before:absolute before:bg-${tooltipTheme.value.backgroundColor} before:w-3
          before:h-xs before:rotate-45`
    )

    const PseudoElementBottomCenterClasses = computed(
      () =>
        `before:-bottom-1.5 before:left-1/2 before:transform before:-translate-x-1/2`
    )

    const PseudoElementBottomLeftClasses = computed(
      () => `before:-bottom-1.5 before:inset-x-3.5`
    )

    const PseudoElementBottomRightClasses = computed(
      () => `before:-bottom-1.5 before:right-3.5`
    )

    const PseudoElementTopCenterClasses = computed(
      () =>
        `before:-top-1.5 before:left-1/2 before:transform before:-translate-x-1/2`
    )

    const PseudoElementTopLeftClasses = computed(
      () => `before:-top-1.5 before:inset-x-3.5`
    )

    const PseudoElementTopRightClasses = computed(
      () => `before:-top-1.5 before:right-3.5`
    )

    const PseudoElementLeftClasses = computed(
      () =>
        `before:top-1/2 before:transform before:-translate-y-1/2 before:-inset-1.5`
    )

    const PseudoElementRightClasses = computed(
      () =>
        `before:top-1/2 before:transform before:-translate-y-1/2 before:-right-1.5`
    )

    const classes = computed(() => ({
      [GeneralClasses]: true,
      [ThemeLightClasses.value]: props.theme === 'light',
      [ThemeDarkClasses.value]: props.theme === 'dark',
      [PseudoElementClasses.value]:
        props.position !== 'none top' && props.position !== 'none bottom',
      [PseudoElementBottomCenterClasses.value]: props.position === 'top',
      [PseudoElementBottomLeftClasses.value]: props.position === 'top-start',
      [PseudoElementBottomRightClasses.value]: props.position === 'top-end',
      [PseudoElementTopCenterClasses.value]: props.position === 'bottom',
      [PseudoElementTopLeftClasses.value]: props.position === 'bottom-start',
      [PseudoElementTopRightClasses.value]: props.position === 'bottom-end',
      [PseudoElementLeftClasses.value]: props.position === 'right',
      [PseudoElementRightClasses.value]: props.position === 'left',
    }))

    useRender(() => (
      <div class="inline-block">
        <div
          class="toggle w-fit h-fit"
          ref={tooltipToggle}
          onClick={(e) => emit('click', e)}
          onMouseenter={() => !props.always && createTooltip()}
          onMouseleave={() => !props.always && destroyTooltip()}
          onTouchstart={() => !props.always && createTooltip()}
          onTouchend={() => !props.always && destroyTooltip()}
        >
          {slots.tooltipToggle?.()}
        </div>
        <div
          ref={tooltipComponent}
          class={[
            slots.supportingText && slots.text ? 'gap-1' : '',
            classes.value,
            'tooltip',
            popperCreated.value ? 'opacity-1' : 'opacity-0',
          ]}
        >
          <div>{slots.text?.()}</div>
          <div>{slots.supportingText?.()}</div>
        </div>
      </div>
    ))

    return {
      tooltipIsVisible,
    }
  },
})

export type UTooltip = InstanceType<typeof UTooltip>
