import { jsx } from 'react/jsx-runtime';
import { forwardRef, useRef, useCallback } from 'react';
import { useReducedMotion, useMotionValue, useSpring, motion } from 'motion/react';
import { cn } from '../../../utils/cn.js';

/**
 * MagneticElement
 *
 * A wrapper component that adds a magnetic pull effect to its children.
 * When the cursor moves over the element, it subtly translates toward
 * the cursor position, creating an engaging interactive effect.
 *
 * The component uses spring-based animations for smooth, natural movement
 * and respects the user's reduced motion preferences.
 *
 * @example
 * ```tsx
 * <MagneticElement strength={0.3}>
 *   <Button>Hover me</Button>
 * </MagneticElement>
 * ```
 *
 * @example
 * ```tsx
 * // With parallax inner content
 * <MagneticElement strength={0.4} innerStrength={2}>
 *   <div className="p-4">
 *     <span>Pull me closer</span>
 *   </div>
 * </MagneticElement>
 * ```
 */
const MagneticElement = forwardRef(({ strength = 0.3, distance = 100, damping = 15, stiffness = 150, innerStrength = 1.5, disabled = false, className, children, onDrag: _onDrag, onDragStart: _onDragStart, onDragEnd: _onDragEnd, onAnimationStart: _onAnimationStart, ...restProps }, ref) => {
    const elementRef = useRef(null);
    const shouldReduceMotion = useReducedMotion();
    const isDisabled = disabled || shouldReduceMotion;
    // Motion values for outer wrapper translation
    const x = useMotionValue(0);
    const y = useMotionValue(0);
    // Spring-animated versions of x and y for smooth movement
    const springConfig = { damping, stiffness, mass: 1 };
    const springX = useSpring(x, springConfig);
    const springY = useSpring(y, springConfig);
    // Motion values for inner content (parallax effect)
    const innerX = useMotionValue(0);
    const innerY = useMotionValue(0);
    const innerSpringX = useSpring(innerX, springConfig);
    const innerSpringY = useSpring(innerY, springConfig);
    const handleMouseMove = useCallback((event) => {
        var _a;
        if (isDisabled)
            return;
        const element = elementRef.current;
        if (!element)
            return;
        const rect = element.getBoundingClientRect();
        const centerX = rect.left + rect.width / 2;
        const centerY = rect.top + rect.height / 2;
        // Calculate distance from cursor to element center
        const deltaX = event.clientX - centerX;
        const deltaY = event.clientY - centerY;
        const distanceFromCenter = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
        // Only apply effect within the configured distance
        const maxDistance = Math.max(rect.width, rect.height) / 2 + distance;
        if (distanceFromCenter < maxDistance) {
            // Apply translation proportional to offset and strength
            const translateX = deltaX * strength;
            const translateY = deltaY * strength;
            x.set(translateX);
            y.set(translateY);
            // Inner content moves slightly more for parallax feel
            if (innerStrength !== 1) {
                innerX.set(translateX * (innerStrength - 1));
                innerY.set(translateY * (innerStrength - 1));
            }
        }
        // Forward to user's onMouseMove if provided
        (_a = restProps.onMouseMove) === null || _a === void 0 ? void 0 : _a.call(restProps, event);
    }, [isDisabled, strength, distance, innerStrength, x, y, innerX, innerY, restProps.onMouseMove]);
    const handleMouseLeave = useCallback((event) => {
        var _a;
        if (isDisabled)
            return;
        // Reset to center position; the spring will animate the return
        x.set(0);
        y.set(0);
        innerX.set(0);
        innerY.set(0);
        // Forward to user's onMouseLeave if provided
        (_a = restProps.onMouseLeave) === null || _a === void 0 ? void 0 : _a.call(restProps, event);
    }, [isDisabled, x, y, innerX, innerY, restProps.onMouseLeave]);
    // When disabled or reduced motion preferred, render without motion
    if (isDisabled) {
        return (jsx("div", { ref: ref, className: cn("inline-block", className), ...restProps, children: children }));
    }
    const hasParallax = innerStrength !== 1;
    return (jsx(motion.div, { ref: (node) => {
            // Assign to both the internal ref and the forwarded ref
            elementRef.current = node;
            if (typeof ref === "function") {
                ref(node);
            }
            else if (ref) {
                ref.current = node;
            }
        }, className: cn("inline-block", className), style: { x: springX, y: springY }, onMouseMove: handleMouseMove, onMouseLeave: handleMouseLeave, ...restProps, children: hasParallax ? (jsx(motion.div, { style: { x: innerSpringX, y: innerSpringY }, children: children })) : (children) }));
});
MagneticElement.displayName = "MagneticElement";

export { MagneticElement };
