import {
  useEffect,
  useRef,
  type CSSProperties,
  type ReactNode,
} from 'react';
import { prefersReducedMotion, SPRINGS } from './utils.js';

export interface MagneticProps {
  /** Child element — typically a button or link. */
  children: ReactNode;
  /** Maximum translation in px. Default 12. */
  strength?: number;
  /** Activation radius around the element. Default 120 px. */
  radius?: number;
  /** Bypass motion entirely (renders without transform). */
  disableMotion?: boolean;
  className?: string;
  style?: CSSProperties;
}

/**
 * `<Magnetic>` — Arc-browser-style pointer attraction wrapper. As the
 * cursor enters a `radius` around the element, the child translates
 * toward it (clamped to `strength`). Resets smoothly on pointer leave.
 *
 * Implementation note: we attach pointer listeners to **window**, not
 * the wrapped element, so the magnet has a "field" — the cursor pulls
 * the child even before hovering it.
 */
export function Magnetic({
  children,
  strength = 12,
  radius = 120,
  disableMotion,
  className,
  style,
}: MagneticProps) {
  const ref = useRef<HTMLSpanElement>(null);
  const reduced = disableMotion ?? prefersReducedMotion();

  useEffect(() => {
    if (reduced) return;
    const el = ref.current;
    if (!el) return;
    const onMove = (e: PointerEvent) => {
      const rect = el.getBoundingClientRect();
      const cx = rect.left + rect.width / 2;
      const cy = rect.top + rect.height / 2;
      const dx = e.clientX - cx;
      const dy = e.clientY - cy;
      const dist = Math.hypot(dx, dy);
      if (dist > radius) {
        el.style.transform = 'translate3d(0,0,0)';
        return;
      }
      const f = (1 - dist / radius) * strength;
      const tx = (dx / Math.max(1, dist)) * f;
      const ty = (dy / Math.max(1, dist)) * f;
      el.style.transform = `translate3d(${tx}px, ${ty}px, 0)`;
    };
    const onLeave = () => {
      el.style.transform = 'translate3d(0,0,0)';
    };
    window.addEventListener('pointermove', onMove);
    window.addEventListener('pointerleave', onLeave);
    return () => {
      window.removeEventListener('pointermove', onMove);
      window.removeEventListener('pointerleave', onLeave);
    };
  }, [reduced, radius, strength]);

  return (
    <span
      ref={ref}
      data-testid="bl-magnetic"
      data-reduced={reduced ? 'true' : 'false'}
      className={className}
      style={{
        display: 'inline-block',
        willChange: 'transform',
        transition: reduced
          ? 'none'
          : `transform 280ms ${SPRINGS.snappy}`,
        ...style,
      }}
    >
      {children}
    </span>
  );
}
