"use client";

/* Shared GSAP wiring for the story sections.
   - Registers ScrollTrigger + the useGSAP helper once.
   - Syncs Lenis smooth-scroll (set up in Providers) with ScrollTrigger so
     scrubbed timelines stay glued to the smoothed scroll position.
   - Exposes a pointer-parallax hook for depth-reactive floating objects. */

import { useEffect, type RefObject } from "react";
import gsap from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";
import { useGSAP } from "@gsap/react";

if (typeof window !== "undefined") {
  gsap.registerPlugin(ScrollTrigger, useGSAP);
}

/* Wire Lenis → ScrollTrigger.update exactly once. Lenis is created in a
   Providers effect, so it may not exist yet on first call — retry on a few
   frames until it appears, then stop. */
let lenisSynced = false;
export function syncLenisScrollTrigger() {
  if (lenisSynced || typeof window === "undefined") return;
  let tries = 0;
  const attach = () => {
    const lenis = (window as unknown as { lenis?: { on?: (e: string, cb: () => void) => void } }).lenis;
    if (lenis?.on) {
      lenis.on("scroll", ScrollTrigger.update);
      lenisSynced = true;
      ScrollTrigger.refresh();
      return;
    }
    if (tries++ < 60) requestAnimationFrame(attach);
  };
  attach();
}

/* Depth-reactive pointer parallax: every element matching `selector` inside
   `scope` drifts toward the cursor by its `data-depth` factor. Uses quickTo
   for a buttery, GPU-friendly follow. Disabled under reduced-motion and on
   touch (no hover). */
export function usePointerParallax(
  scope: RefObject<HTMLElement | null>,
  selector: string,
) {
  useEffect(() => {
    const root = scope.current;
    if (!root) return;
    if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;
    if (!window.matchMedia("(hover: hover)").matches) return;

    const targets = Array.from(root.querySelectorAll<HTMLElement>(selector));
    const movers = targets.map((el) => ({
      x: gsap.quickTo(el, "x", { duration: 0.9, ease: "power3.out" }),
      y: gsap.quickTo(el, "y", { duration: 0.9, ease: "power3.out" }),
      depth: parseFloat(el.dataset.depth || "0.05"),
    }));

    const onMove = (e: PointerEvent) => {
      const r = root.getBoundingClientRect();
      const dx = (e.clientX - (r.left + r.width / 2));
      const dy = (e.clientY - (r.top + r.height / 2));
      movers.forEach((m) => {
        m.x(dx * m.depth);
        m.y(dy * m.depth);
      });
    };
    const onLeave = () => movers.forEach((m) => { m.x(0); m.y(0); });

    root.addEventListener("pointermove", onMove);
    root.addEventListener("pointerleave", onLeave);
    return () => {
      root.removeEventListener("pointermove", onMove);
      root.removeEventListener("pointerleave", onLeave);
    };
  }, [scope, selector]);
}

/* Splits a phrase into per-word masks: each word sits in an overflow-hidden
   box so a GSAP tween on `.${wordClass}` can slide/flip it out of the mask.
   `inner` lets a segment carry its own colour. */
export function MaskWords({
  text,
  wordClass,
  inner = "",
}: {
  text: string;
  wordClass: string;
  inner?: string;
}) {
  return (
    <>
      {text.split(" ").map((w, i) => (
        <span
          key={i}
          className="mr-[0.24em] inline-block overflow-hidden align-bottom pb-[0.08em]"
        >
          <span className={`${wordClass} inline-block ${inner}`}>{w}</span>
        </span>
      ))}
    </>
  );
}

export { gsap, ScrollTrigger, useGSAP };
