<script setup>
import { computed, nextTick, onBeforeUnmount, ref } from 'vue'

const props = defineProps({
  title: {
    type: String,
    required: true,
  },
  description: {
    type: String,
    required: true,
  },
  visible: {
    type: Boolean,
    default: true,
  },
  dismissible: {
    type: Boolean,
    default: true,
  },
  buttonLabel: {
    type: String,
    default: '?',
  },
})

const emit = defineEmits(['dismiss'])

const open = ref(false)
const rootRef = ref(null)
const buttonRef = ref(null)
const popupStyle = ref({})
const isCompactButtonLabel = computed(() => String(props.buttonLabel || '').trim().length <= 1)

function close() {
  open.value = false
}

function updatePopupPosition() {
  if (!open.value || !buttonRef.value || typeof window === 'undefined') {
    return
  }

  const rect = buttonRef.value.getBoundingClientRect()
  const viewportWidth = window.innerWidth
  const preferredWidth = Math.min(256, Math.max(196, viewportWidth - 24))
  const horizontalPadding = 12
  let left = rect.left

  if (left + preferredWidth > viewportWidth - horizontalPadding) {
    left = viewportWidth - preferredWidth - horizontalPadding
  }

  if (left < horizontalPadding) {
    left = horizontalPadding
  }

  popupStyle.value = {
    left: `${left}px`,
    top: `${rect.bottom + 8}px`,
    width: `${preferredWidth}px`,
    maxWidth: `calc(100vw - ${horizontalPadding * 2}px)`,
  }
}

async function toggle() {
  if (!props.visible) {
    return
  }

  open.value = !open.value

  if (open.value) {
    await nextTick()
    updatePopupPosition()
  }
}

function closeFromOutside(event) {
  if (!open.value || rootRef.value?.contains(event.target)) {
    return
  }

  close()
}

function dismiss() {
  emit('dismiss')
  close()
}

if (typeof window !== 'undefined') {
  window.addEventListener('pointerdown', closeFromOutside)
  window.addEventListener('resize', updatePopupPosition)
  window.addEventListener('scroll', updatePopupPosition, true)
}

onBeforeUnmount(() => {
  window.removeEventListener('pointerdown', closeFromOutside)
  window.removeEventListener('resize', updatePopupPosition)
  window.removeEventListener('scroll', updatePopupPosition, true)
})
</script>

<template>
  <span
    v-if="visible"
    ref="rootRef"
    class="relative inline-flex print:hidden"
  >
    <button
      ref="buttonRef"
      type="button"
      class="inline-flex shrink-0 items-center justify-center whitespace-nowrap border border-stone-300 bg-white text-[10px] font-bold text-stone-500 transition hover:border-stone-500 hover:text-stone-900 focus-visible:ring-2 focus-visible:ring-stone-900 focus-visible:ring-offset-2"
      :class="isCompactButtonLabel ? 'h-5 w-5 rounded-full' : 'min-h-[22px] rounded-full px-2 py-1 leading-none'"
      aria-label="가이드 보기"
      :aria-expanded="open"
      @click.stop="toggle"
    >
      {{ buttonLabel }}
    </button>

    <Teleport to="body">
      <span
        v-if="open"
        class="fixed z-[120] rounded-2xl border border-stone-200 bg-white p-4 text-left shadow-[0_18px_50px_rgba(28,25,23,0.16)] print:hidden"
        :style="popupStyle"
        @pointerdown.stop
      >
        <span class="block text-[10px] font-bold uppercase tracking-[0.2em] text-stone-500">{{ title }}</span>
        <span class="mt-2 block break-words text-[11px] font-semibold leading-5 tracking-[0.04em] text-stone-700">{{ description }}</span>
        <button
          v-if="dismissible"
          type="button"
          class="mt-3 rounded-full border border-stone-200 px-3 py-2 text-[10px] font-bold tracking-[0.14em] text-stone-600 transition hover:border-stone-500 hover:text-stone-900"
          @click="dismiss"
        >
          더 이상 보지 않기
        </button>
      </span>
    </Teleport>
  </span>
</template>
