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

export interface ParallaxProps {
  /** Slot moved on scroll. */
  children: ReactNode;
  /**
   * Translation speed multiplier. 0 = stationary, 1 = scrolls with the
   * page (default), 0.5 = scrolls half-speed, -0.5 = scrolls upward.
   * Sensible range: −1 to 1.
   */
  speed?: number;
  /** Translation axis. Default `'y'`. */
  axis?: 'y' | 'x';
  /** Bypass entirely — useful for tests / reduced-motion. */
  disableMotion?: boolean;
  className?: string;
  style?: CSSProperties;
}

/**
 * `<Parallax>` — scroll-driven translation wrapper.
 *
 * Uses `transform: translate3d(…)` updated inside a `requestAnimationFrame`
 * loop driven by `window.scroll`. The transform is computed relative to
 * the element's vertical centre crossing the viewport's centre — so the
 * effect peaks while the element is on-screen and never accumulates
 * unbounded translation.
 *
 * Honours `prefers-reduced-motion`: with `disableMotion` (or the OS
 * setting) the wrapper renders un-transformed.
 *
 * Wave 13.D.1. Zero deps, ~1.4 KB gzipped. Pairs with `<TiltCard>`
 * and `<TiltGallery>` for the spatial-hero pattern.
 */
export function Parallax({
  children,
  speed = 0.3,
  axis = 'y',
  disableMotion,
  className,
  style,
}: ParallaxProps) {
  const ref = useRef<HTMLDivElement>(null);
  const reduced = disableMotion ?? prefersReducedMotion();

  useEffect(() => {
    if (reduced) return;
    const el = ref.current;
    if (!el) return;
    let raf = 0;
    const apply = () => {
      raf = 0;
      const rect = el.getBoundingClientRect();
      const vh = window.innerHeight || 1;
      // Distance, in px, of the element's centre from viewport centre.
      const delta = rect.top + rect.height / 2 - vh / 2;
      const t = -delta * speed;
      el.style.transform =
        axis === 'y'
          ? `translate3d(0, ${t.toFixed(2)}px, 0)`
          : `translate3d(${t.toFixed(2)}px, 0, 0)`;
    };
    const onScroll = () => {
      if (raf) return;
      raf = window.requestAnimationFrame(apply);
    };
    apply();
    window.addEventListener('scroll', onScroll, { passive: true });
    window.addEventListener('resize', onScroll);
    return () => {
      window.removeEventListener('scroll', onScroll);
      window.removeEventListener('resize', onScroll);
      if (raf) window.cancelAnimationFrame(raf);
      el.style.transform = '';
    };
  }, [reduced, speed, axis]);

  return (
    <div
      ref={ref}
      data-testid="bl-parallax"
      data-reduced={reduced ? 'true' : 'false'}
      data-axis={axis}
      className={className}
      style={{ willChange: reduced ? undefined : 'transform', ...style }}
    >
      {children}
    </div>
  );
}
