<script lang="ts">
  interface Props {
    value: number;
    min?: number;
    max?: number;
    step?: number;
    formatValue?: (v: number) => string;
    onchange?: (value: number) => void;
    label?: string;
  }

  let {
    value = $bindable(),
    min = 0,
    max = 100,
    step = 1,
    formatValue = (v: number) => String(v),
    onchange,
    label = "Value",
  }: Props = $props();

  let holdTimer: ReturnType<typeof setTimeout> | null = null;
  let holdInterval: ReturnType<typeof setInterval> | null = null;

  function decrement() {
    if (value > min) {
      value = Math.max(min, value - step);
      onchange?.(value);
    }
  }

  function increment() {
    if (value < max) {
      value = Math.min(max, value + step);
      onchange?.(value);
    }
  }

  function startHold(fn: () => void) {
    fn();
    // Initial delay before repeating
    holdTimer = setTimeout(() => {
      // Start repeating, accelerate over time
      let delay = 150;
      function tick() {
        fn();
        delay = Math.max(40, delay * 0.85);
        holdInterval = setTimeout(tick, delay) as unknown as ReturnType<typeof setInterval>;
      }
      tick();
    }, 400);
  }

  function stopHold() {
    if (holdTimer) { clearTimeout(holdTimer); holdTimer = null; }
    if (holdInterval) { clearTimeout(holdInterval as unknown as ReturnType<typeof setTimeout>); holdInterval = null; }
  }
</script>

<div class="flex items-center gap-1.5" role="group" aria-label={label}>
  <button
    type="button"
    aria-label="Decrease"
    class="flex h-7 w-7 items-center justify-center rounded-lg
           bg-[#141414] text-[#8a8a8a] transition-colors
           hover:bg-[#1c1c1c] hover:text-white
           disabled:opacity-20"
    onmousedown={() => startHold(decrement)}
    onmouseup={stopHold}
    onmouseleave={stopHold}
    onclick={(e) => { if (e.detail === 0) decrement(); }}
    disabled={value <= min}
  >
    &minus;
  </button>
  <span class="min-w-[38px] text-center text-[13px] tabular-nums text-white" aria-live="polite" aria-atomic="true">
    {formatValue(value)}
  </span>
  <button
    type="button"
    aria-label="Increase"
    class="flex h-7 w-7 items-center justify-center rounded-lg
           bg-[#141414] text-[#8a8a8a] transition-colors
           hover:bg-[#1c1c1c] hover:text-white
           disabled:opacity-20"
    onmousedown={() => startHold(increment)}
    onmouseup={stopHold}
    onmouseleave={stopHold}
    onclick={(e) => { if (e.detail === 0) increment(); }}
    disabled={value >= max}
  >
    +
  </button>
</div>
