/**
 * AnimatedCounter - Animated number display with count-up effect
 *
 * Uses requestAnimationFrame for smooth 60fps animation.
 * Respects prefers-reduced-motion.
 */

import { useState, useEffect, useRef, useSyncExternalStore } from 'react'
import { cn } from './utils'

// --- Inlined useReducedMotion hook ---

const REDUCED_MOTION_QUERY = '(prefers-reduced-motion: reduce)'

function getSnapshot(): boolean {
  if (typeof window === 'undefined') return false
  return window.matchMedia(REDUCED_MOTION_QUERY).matches
}

function getServerSnapshot(): boolean {
  return false
}

function subscribe(callback: () => void): () => void {
  const mediaQuery = window.matchMedia(REDUCED_MOTION_QUERY)
  mediaQuery.addEventListener('change', callback)
  return () => mediaQuery.removeEventListener('change', callback)
}

function useReducedMotion(): boolean {
  return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot)
}

// --- Inlined useCountUp hook ---

interface UseCountUpOptions {
  start?: number
  duration?: number
  delay?: number
  decimals?: number
  easing?: (t: number) => number
}

const easeOutCubic = (t: number): number => {
  return 1 - Math.pow(1 - t, 3)
}

function useCountUp(end: number, options: UseCountUpOptions = {}): number {
  const { start = 0, duration = 1000, delay = 0, decimals = 0, easing = easeOutCubic } = options

  const prefersReducedMotion = useReducedMotion()
  const frameRef = useRef<number | undefined>(undefined)
  const startTimeRef = useRef<number | undefined>(undefined)

  const initialValue = prefersReducedMotion ? end : start
  const [count, setCount] = useState(initialValue)

  useEffect(() => {
    if (prefersReducedMotion) {
      return
    }

    const startAnimation = () => {
      startTimeRef.current = undefined

      const animate = (currentTime: number) => {
        if (!startTimeRef.current) {
          startTimeRef.current = currentTime
        }

        const elapsed = currentTime - startTimeRef.current
        const progress = Math.min(elapsed / duration, 1)
        const easedProgress = easing(progress)

        const currentValue = start + (end - start) * easedProgress

        const roundedValue =
          decimals > 0
            ? Math.round(currentValue * Math.pow(10, decimals)) / Math.pow(10, decimals)
            : Math.round(currentValue)

        setCount(roundedValue)

        if (progress < 1) {
          frameRef.current = requestAnimationFrame(animate)
        }
      }

      frameRef.current = requestAnimationFrame(animate)
    }

    const timeoutId = setTimeout(startAnimation, delay)

    return () => {
      clearTimeout(timeoutId)
      if (frameRef.current) {
        cancelAnimationFrame(frameRef.current)
      }
    }
  }, [end, start, duration, delay, decimals, easing, prefersReducedMotion])

  return count
}

// --- AnimatedCounter components ---

interface AnimatedCounterProps {
  /** Target value to count to */
  value: number
  /** Animation duration in ms */
  duration?: number
  /** Delay before starting animation */
  delay?: number
  /** Number of decimal places */
  decimals?: number
  /** Prefix to display before the number (e.g., "$") */
  prefix?: string
  /** Suffix to display after the number (e.g., "%") */
  suffix?: string
  /** Additional CSS classes */
  className?: string | undefined
  /** Whether to animate only when in view */
  animateOnView?: boolean
}

export function AnimatedCounter({
  value,
  duration = 1000,
  delay = 0,
  decimals = 0,
  prefix = '',
  suffix = '',
  className,
}: Omit<AnimatedCounterProps, 'animateOnView'>) {
  const displayValue = useCountUp(value, { duration, delay, decimals })

  const formattedValue =
    decimals > 0 ? displayValue.toFixed(decimals) : displayValue.toLocaleString()

  return (
    <span className={cn('tabular-nums', className)} aria-live="polite" aria-atomic="true">
      {prefix}
      {formattedValue}
      {suffix}
    </span>
  )
}

/**
 * AnimatedPercentage - Specialized counter for percentage values
 */
export function AnimatedPercentage({
  value,
  duration = 1000,
  delay = 0,
  className,
}: Omit<AnimatedCounterProps, 'suffix' | 'decimals' | 'prefix' | 'animateOnView'>) {
  return (
    <AnimatedCounter
      value={value}
      duration={duration}
      delay={delay}
      decimals={0}
      suffix="%"
      className={className}
    />
  )
}

/**
 * AnimatedScore - Score display with color based on value
 */
export function AnimatedScore({
  value,
  duration = 1000,
  delay = 0,
  className,
  showSuffix = true,
}: Omit<AnimatedCounterProps, 'suffix' | 'decimals' | 'prefix' | 'animateOnView'> & {
  showSuffix?: boolean
}) {
  const getScoreColor = (score: number) => {
    if (score >= 80) return 'text-emerald-400'
    if (score >= 60) return 'text-amber-400'
    if (score >= 40) return 'text-orange-400'
    return 'text-rose-400'
  }

  return (
    <AnimatedCounter
      value={value}
      duration={duration}
      delay={delay}
      decimals={0}
      suffix={showSuffix ? '%' : ''}
      className={cn(getScoreColor(value), className)}
    />
  )
}
