<script setup lang="ts">
// 숫자 카운트업 — 요소가 보이면 0부터 목표값까지 증가 애니메이션
// SSR/no-JS 환경에서는 최종값을 그대로 렌더(SEO·접근성), 클라이언트에서만 애니메이션
import { ref, computed, onMounted, onBeforeUnmount } from 'vue'

interface Props {
  value: number
  duration?: number // ms
  locale?: boolean // 천 단위 구분기호(ko-KR)
}
const props = withDefaults(defineProps<Props>(), {
  duration: 1600,
  locale: true,
})

const display = ref<number>(props.value) // 초기값 = 최종값 (SSR 노출)
const elRef = ref<HTMLElement | null>(null)
let io: IntersectionObserver | null = null
let rafId = 0

const formatted = computed(() =>
  props.locale ? display.value.toLocaleString('ko-KR') : String(display.value),
)

function animate() {
  const start = performance.now()
  const from = 0
  const to = props.value
  const tick = (now: number) => {
    const t = Math.min(1, (now - start) / props.duration)
    // easeOutCubic — 빠르게 시작해 부드럽게 감속
    const eased = 1 - Math.pow(1 - t, 3)
    display.value = Math.round(from + (to - from) * eased)
    if (t < 1) rafId = requestAnimationFrame(tick)
  }
  rafId = requestAnimationFrame(tick)
}

onMounted(() => {
  const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches
  if (reduceMotion || !elRef.value) return

  display.value = 0 // 클라이언트에서 0부터 시작
  io = new IntersectionObserver(
    (entries) => {
      for (const entry of entries) {
        if (entry.isIntersecting) {
          animate()
          io?.disconnect()
        }
      }
    },
    { threshold: 0.4 },
  )
  io.observe(elRef.value)
})

onBeforeUnmount(() => {
  io?.disconnect()
  if (rafId) cancelAnimationFrame(rafId)
})
</script>

<template>
  <span ref="elRef">{{ formatted }}</span>
</template>
