/**
 * useCountUp — RAF-tween анимация числа (Quiet Luxury KPI cards).
 *
 * - easeOutQuint easing
 * - respects prefers-reduced-motion (instant value)
 * - re-animates when target ref changes
 *
 * Spec: docs/superpowers/plans/2026-05-12-portal-redesign-quiet-luxury-plan.md (Task 6).
 */
import { ref, watch, type Ref } from 'vue';

export interface CountUpOptions {
    duration?: number; // ms
    precision?: number; // знаков после запятой
}

export interface CountUpHandle {
    display: Ref<number>;
    start: () => void;
}

const easeOutQuint = (t: number): number => 1 - Math.pow(1 - t, 5);

function prefersReducedMotion(): boolean {
    if (typeof window === 'undefined' || !window.matchMedia) return false;
    return window.matchMedia('(prefers-reduced-motion: reduce)').matches;
}

export function useCountUp(target: Ref<number>, opts: CountUpOptions = {}): CountUpHandle {
    const duration = opts.duration ?? 600;
    const precision = opts.precision ?? 0;
    const display = ref(0);
    let raf: number | null = null;
    let startTime = 0;
    let fromValue = 0;

    function tick(now: number): void {
        const elapsed = now - startTime;
        const t = Math.min(elapsed / duration, 1);
        const eased = easeOutQuint(t);
        const value = fromValue + (target.value - fromValue) * eased;
        display.value = precision === 0 ? Math.round(value) : parseFloat(value.toFixed(precision));
        if (t < 1) {
            raf = requestAnimationFrame(tick);
        } else {
            display.value = target.value;
            raf = null;
        }
    }

    function start(): void {
        if (prefersReducedMotion()) {
            display.value = target.value;
            return;
        }
        if (raf !== null) cancelAnimationFrame(raf);
        fromValue = display.value;
        startTime = performance.now();
        raf = requestAnimationFrame(tick);
    }

    watch(target, () => {
        if (display.value !== target.value) start();
    });

    return { display, start };
}
