<template>
  <div class="relative inline-block w-full">
    <span
      ref="textEl"
      class="overflow-hidden text-nowrap block"
      :class="props.hideEllipsis ? 'text-clip mr-3 text-gray-400' : 'overflow-ellipsis'"
    >
      {{ displayedText }}
    </span>

    <!-- Invisible hover area positioned over the ellipsis -->
    <Tooltip v-if="isTruncated && !props.hideEllipsis">
      <TooltipRoot>
        <TooltipTrigger asChild>
          <div
            class="absolute right-0 top-0 bottom-0 w-6 cursor-default"
            :aria-label="displayedText"
          />
        </TooltipTrigger>
        <TooltipContent
          hide-arrow
          class="!max-w-[300px] !whitespace-normal !break-words !w-auto !text-wrap"
        >
          {{ displayedText }}
        </TooltipContent>
      </TooltipRoot>
    </Tooltip>
  </div>
</template>

<script setup lang="ts">
import { ref, watch, onMounted, onUnmounted, nextTick } from 'vue'
import { Tooltip, TooltipTrigger, TooltipContent } from '@/components/ui/tooltip'

interface Props {
  text: string
  speed?: number
  delay?: number
  hideEllipsis?: boolean
}

const props = withDefaults(defineProps<Props>(), {
  speed: 50,
  delay: 0,
})

const displayedText = ref('')
const isTyping = ref(false)
let typewriterTimeout: ReturnType<typeof setTimeout> | null = null

const textEl = ref<HTMLElement | null>(null)
const isTruncated = ref(false)

function checkTruncation() {
  if (!textEl.value) {
    isTruncated.value = false
    return
  }
  // Only meaningful when ellipsis behavior is enabled
  if (props.hideEllipsis) {
    isTruncated.value = false
    return
  }
  const el = textEl.value
  isTruncated.value = el.scrollWidth > el.clientWidth
}

function startTypewriter() {
  if (typewriterTimeout) clearTimeout(typewriterTimeout)
  displayedText.value = ''
  isTyping.value = true
  let currentIndex = 0

  const typeNextChar = () => {
    if (currentIndex < props.text.length) {
      displayedText.value += props.text[currentIndex]
      currentIndex++
      typewriterTimeout = setTimeout(typeNextChar, props.speed)
    } else {
      isTyping.value = false
      nextTick(checkTruncation)
    }
  }

  if (props.delay > 0) {
    typewriterTimeout = setTimeout(typeNextChar, props.delay)
  } else {
    typeNextChar()
  }
}

watch(
  () => props.text,
  (newText, oldText) => {
    if (newText !== oldText && oldText !== undefined) {
      startTypewriter()
    } else {
      nextTick(checkTruncation)
    }
  }
)

watch(
  () => props.hideEllipsis,
  () => nextTick(checkTruncation)
)

onMounted(() => {
  displayedText.value = props.text
  nextTick(checkTruncation)
  window.addEventListener('resize', checkTruncation)
})

onUnmounted(() => {
  if (typewriterTimeout) clearTimeout(typewriterTimeout)
  window.removeEventListener('resize', checkTruncation)
})
</script>

<style scoped>
@keyframes blink {
  0%,
  50% {
    opacity: 1;
  }
  51%,
  100% {
    opacity: 0;
  }
}

.custom-tooltip {
  max-width: 300px !important;
  white-space: normal !important;
  word-wrap: break-word !important;
  overflow-wrap: break-word !important;
  width: auto !important;
}
</style>
