'use client'

import { motion, type HTMLMotionProps } from 'motion/react'
import { type ElementType, type ReactNode, useCallback, useRef, useState } from 'react'

type MotionComponent = 'div' | 'section' | 'p' | 'h1' | 'h2' | 'h3' | 'span' | 'a' | 'article' | 'nav' | 'header' | 'footer' | 'aside'
type Direction = 'up' | 'down' | 'left' | 'right'

const directionOffsets: Record<Direction, { x?: number; y?: number }> = {
    up: { y: 20 },
    down: { y: -20 },
    left: { x: 20 },
    right: { x: -20 },
}

interface AnimateInProps {
    children?: ReactNode
    delay?: number
    viewportDelay?: number
    duration?: number
    className?: string
    as?: MotionComponent
    direction?: Direction
    onScroll?: boolean
}

export function AnimateIn({
    children,
    delay = 0,
    viewportDelay,
    duration = 0.5,
    className,
    as = 'div',
    direction = 'up',
    onScroll = true
}: AnimateInProps) {
    const Component = motion[as] as ElementType<HTMLMotionProps<typeof as>>
    const offset = directionOffsets[direction]
    const [actualDelay, setActualDelay] = useState(delay)
    const initializedRef = useRef(false)

    const callbackRef = useCallback((node: HTMLElement | null) => {
        if (!node || initializedRef.current || viewportDelay === undefined) return

        initializedRef.current = true
        const rect = node.getBoundingClientRect()
        const isInViewport = rect.top < window.innerHeight && rect.bottom > 0

        if (isInViewport) {
            setActualDelay(viewportDelay)
        }
    }, [viewportDelay])

    const animationProps = onScroll
        ? {
            initial: { opacity: 0, ...offset },
            whileInView: { opacity: 1, x: 0, y: 0 },
            viewport: { once: true, margin: '-50px' },
        }
        : {
            initial: { opacity: 0, ...offset },
            animate: { opacity: 1, x: 0, y: 0 },
        }

    return (
        <Component
            ref={ callbackRef }
            { ...animationProps }
            transition={{
                duration,
                delay: actualDelay,
                ease: [0.25, 0.1, 0.25, 1]
            }}
            className={ className }
        >
            { children }
        </Component>
    )
}
