<template>
  <div
    class="circle-button-wrap"
    @mouseenter="onShow"
    @mouseleave="onHide"
    @focusin="onShow"
    @focusout="onHide"
  >
    <button
      ref="triggerRef"
      :type="type"
      class="circle-button"
      :style="buttonStyle"
      :disabled="disabled || loading"
      @click="handleClick"
    >
      <slot name="icon" />
    </button>

    <Teleport to="body">
      <Transition name="tooltip-fade">
        <div
          v-if="showTooltip && tooltip"
          ref="tooltipRef"
          class="circle-button__tooltip"
          :class="`circle-button__tooltip--${actualPlacement}`"
          :style="tooltipStyle"
          role="tooltip"
        >
          {{ tooltip }}
          <span class="circle-button__tooltip-arrow" :style="arrowStyle" />
        </div>
      </Transition>
    </Teleport>
  </div>
</template>

<script setup>
import { computed, nextTick, onBeforeUnmount, ref, watch } from 'vue'
import { arrow, autoUpdate, computePosition, flip, offset, shift } from '@floating-ui/dom'

const props = defineProps({
  type: { type: String, default: 'button' },
  tooltip: { type: String, default: '' },
  tooltipPosition: {
    type: String,
    default: 'top',
    validator: (v) => ['top', 'bottom'].includes(v),
  },
  bgColor: { type: String, default: '#fff' },
  size: { type: String, default: '2.75rem' },
  disabled: { type: Boolean, default: false },
  loading: { type: Boolean, default: false },
})

const emit = defineEmits(['click'])

const showTooltip = ref(false)
const triggerRef = ref(null)
const tooltipRef = ref(null)
const arrowEl = ref(null)
const tooltipStyle = ref({ position: 'fixed', top: '0', left: '0' })
const arrowStyle = ref({})
const actualPlacement = ref(props.tooltipPosition)

const buttonStyle = computed(() => ({
  width: props.size,
  height: props.size,
  backgroundColor: props.bgColor,
}))

let cleanupAutoUpdate = null

const updatePosition = async () => {
  if (!triggerRef.value || !tooltipRef.value) return
  arrowEl.value = tooltipRef.value.querySelector('.circle-button__tooltip-arrow')
  const middleware = [offset(6), flip(), shift({ padding: 8 })]
  if (arrowEl.value) middleware.push(arrow({ element: arrowEl.value }))

  const result = await computePosition(triggerRef.value, tooltipRef.value, {
    placement: props.tooltipPosition,
    strategy: 'fixed',
    middleware,
  })

  tooltipStyle.value = {
    position: 'fixed',
    top: `${result.y}px`,
    left: `${result.x}px`,
  }
  actualPlacement.value = result.placement

  const arrowData = result.middlewareData.arrow
  if (arrowEl.value && arrowData) {
    const side = result.placement.split('-')[0]
    const oppositeSide = { top: 'bottom', bottom: 'top', left: 'right', right: 'left' }[side]
    arrowStyle.value = {
      left: arrowData.x == null ? '' : `${arrowData.x}px`,
      top: arrowData.y == null ? '' : `${arrowData.y}px`,
      [oppositeSide]: '-0.25rem',
    }
  }
}

const startAutoUpdate = () => {
  if (!triggerRef.value || !tooltipRef.value) return
  cleanupAutoUpdate?.()
  cleanupAutoUpdate = autoUpdate(triggerRef.value, tooltipRef.value, updatePosition)
}

const stopAutoUpdate = () => {
  cleanupAutoUpdate?.()
  cleanupAutoUpdate = null
}

watch(showTooltip, async (open) => {
  if (open) {
    await nextTick()
    startAutoUpdate()
  } else {
    stopAutoUpdate()
  }
})

onBeforeUnmount(stopAutoUpdate)

const onShow = () => {
  if (!props.tooltip) return
  showTooltip.value = true
}
const onHide = () => {
  showTooltip.value = false
}

const handleClick = (event) => {
  if (props.disabled || props.loading) {
    event.preventDefault()
    return
  }
  emit('click', event)
}
</script>

<style lang="scss" scoped>
.circle-button-wrap {
  position: relative;
  display: inline-block;
}

.circle-button {
  display: flex;
  align-items: center;
  justify-content: center;
  border: none;
  border-radius: 9999px;
  cursor: pointer;
  padding: 0;
  z-index: 20;

  &:disabled {
    opacity: 0.5;
    cursor: not-allowed;
  }
}
</style>

<style lang="scss">
.circle-button__tooltip {
  z-index: 1000;
  white-space: nowrap;
  padding: 0.25rem 0.625rem;
  font-size: 0.75rem;
  font-weight: 500;
  color: #fff;
  background-color: #000;
  border-radius: 0.375rem;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 5%);
  pointer-events: none;
}

.circle-button__tooltip-arrow {
  position: absolute;
  width: 0.5rem;
  height: 0.5rem;
  background-color: #000;
  transform: rotate(45deg);
}

.tooltip-fade-enter-active,
.tooltip-fade-leave-active {
  transition: opacity 0.15s ease;
}

.tooltip-fade-enter-from,
.tooltip-fade-leave-to {
  opacity: 0;
}
</style>
