import { useMemo, forwardRef, useRef } from 'react'
import { motion, useInView, type HTMLMotionProps } from 'framer-motion'
import { cn } from '../lib/utils'

export interface ShimmeringTextProps extends Omit<HTMLMotionProps<'span'>, 'children'> {
  /** Required. Text to display with shimmer effect */
  text: string
  /** Animation duration in seconds. Default: 2 */
  duration?: number
  /** Delay before starting animation in seconds. Default: 0 */
  delay?: number
  /** Whether to repeat the animation. Default: true */
  repeat?: boolean
  /** Pause duration between repeats in seconds. Default: 0.5 */
  repeatDelay?: number
  /** Whether to start animation when entering viewport. Default: true */
  startOnView?: boolean
  /** Whether to animate only once. Default: false */
  once?: boolean
  /** Margin for viewport detection (e.g., "0px 0px -10%"). Default: undefined */
  inViewMargin?: string
  /** Shimmer spread multiplier. Default: 2 */
  spread?: number
  /** Base text color (CSS color value) */
  color?: string
  /** Shimmer gradient color (CSS color value) */
  shimmerColor?: string
}

const ShimmeringText = forwardRef<HTMLSpanElement, ShimmeringTextProps>(
  (
    {
      text,
      duration = 2,
      delay = 0,
      repeat = true,
      repeatDelay = 0.5,
      startOnView = true,
      once = false,
      inViewMargin,
      spread = 2,
      color,
      shimmerColor,
      className,
      style,
      ...props
    },
    ref
  ) => {
    const containerRef = useRef<HTMLSpanElement>(null)
    const isInView = useInView(containerRef, {
      once: once,
      margin: inViewMargin as `${number}px ${number}px ${number}px ${number}px` | undefined,
    })

    const shouldAnimate = startOnView ? isInView : true

    const dynamicSpread = useMemo(() => {
      return text.length * spread
    }, [text, spread])

    const baseColor = color || 'var(--color-text-secondary)'
    const highlightColor = shimmerColor || 'var(--color-text)'

    const backgroundStyle = useMemo(() => {
      return {
        backgroundImage: `linear-gradient(90deg, transparent calc(50% - ${dynamicSpread}px), ${highlightColor}, transparent calc(50% + ${dynamicSpread}px)), linear-gradient(${baseColor}, ${baseColor})`,
      }
    }, [dynamicSpread, highlightColor, baseColor])

    const combinedRef = (node: HTMLSpanElement | null) => {
      ;(containerRef as React.MutableRefObject<HTMLSpanElement | null>).current = node
      if (typeof ref === 'function') {
        ref(node)
      } else if (ref) {
        ref.current = node
      }
    }

    return (
      <motion.span
        ref={combinedRef}
        className={cn(
          'relative inline-block bg-clip-text text-transparent',
          'bg-[length:250%_100%,auto] bg-no-repeat',
          className
        )}
        style={{
          ...backgroundStyle,
          ...style,
        }}
        initial={{ backgroundPosition: '100% center' }}
        animate={
          shouldAnimate
            ? { backgroundPosition: '0% center' }
            : { backgroundPosition: '100% center' }
        }
        transition={{
          repeat: repeat && !once ? Infinity : 0,
          repeatDelay: repeatDelay,
          duration: duration,
          delay: delay,
          ease: 'linear',
        }}
        {...props}
      >
        {text}
      </motion.span>
    )
  }
)

ShimmeringText.displayName = 'ShimmeringText'

export { ShimmeringText }
