<template>
  <span class="bot-counter">
    <span
      v-for="(ch, i) in chars"
      :key="i"
      class="slot"
      :class="ch === '.' ? 'dot' : 'digit'"
    >{{ ch }}</span><span class="pct">%</span>
  </span>
</template>

<script setup lang="ts">
import { ref, computed, onMounted, onBeforeUnmount } from 'vue'

const props = withDefaults(defineProps<{
  start?: number       // starting value (must be < ceiling)
  ceiling?: number     // asymptotic ceiling (e.g. 50 — value approaches but never reaches)
  tau?: number         // time constant in seconds — smaller = faster approach
  digits?: number      // decimal places shown
}>(), {
  start: 49.42,
  ceiling: 50,
  tau: 600,
  digits: 6,
})

const elapsed = ref(0)
let raf: number | null = null
let startTime: number | null = null

function tick(now: number) {
  if (startTime === null) startTime = now
  elapsed.value = (now - startTime) / 1000
  raf = requestAnimationFrame(tick)
}

onMounted(() => {
  startTime = null
  raf = requestAnimationFrame(tick)
})

onBeforeUnmount(() => {
  if (raf !== null) cancelAnimationFrame(raf)
})

// Asymptotic: v(t) = ceiling - (ceiling - start) * exp(-t/tau)
// Monotonically increases, never reaches ceiling.
// At t=0 the per-second rate is (ceiling - start) / tau, which seeds
// fast churn in the trailing decimals.
const display = computed(() => {
  const gap = props.ceiling - props.start
  const v = props.ceiling - gap * Math.exp(-elapsed.value / props.tau)
  return v.toFixed(props.digits)
})

const chars = computed(() => display.value.split(''))
</script>

<style scoped>
.bot-counter {
  display: inline-block;
  white-space: nowrap;
}
.slot {
  display: inline-block;
  text-align: center;
}
.slot.digit {
  width: 0.58em;
}
.slot.dot {
  width: 0.22em;
}
.pct {
  font-size: 0.7em;
  margin-left: 0.12em;
  vertical-align: 0.08em;
  opacity: 0.8;
}
</style>
