
// Custom cursor + magnetic link components

function CustomCursor() {
  const cursorRef = React.useRef(null);
  const dotRef = React.useRef(null);
  const pos = React.useRef({ x: -100, y: -100 });
  const target = React.useRef({ x: -100, y: -100 });
  const raf = React.useRef(null);

  React.useEffect(() => {
    const onMove = (e) => {
      target.current = { x: e.clientX, y: e.clientY };
    };

    const onEnterInteractive = () => {
      cursorRef.current?.classList.add("cursor--hover");
    };
    const onLeaveInteractive = () => {
      cursorRef.current?.classList.remove("cursor--hover");
    };
    const onEnterLink = () => {
      cursorRef.current?.classList.add("cursor--link");
    };
    const onLeaveLink = () => {
      cursorRef.current?.classList.remove("cursor--link");
    };

    const attachListeners = () => {
      document.querySelectorAll("button, [data-magnetic]").forEach((el) => {
        el.addEventListener("mouseenter", onEnterInteractive);
        el.addEventListener("mouseleave", onLeaveInteractive);
      });
      document.querySelectorAll("a, .cursor-link").forEach((el) => {
        el.addEventListener("mouseenter", onEnterLink);
        el.addEventListener("mouseleave", onLeaveLink);
      });
    };

    const observer = new MutationObserver(attachListeners);
    observer.observe(document.body, { childList: true, subtree: true });
    attachListeners();

    window.addEventListener("mousemove", onMove);

    const loop = () => {
      pos.current.x += (target.current.x - pos.current.x) * 0.12;
      pos.current.y += (target.current.y - pos.current.y) * 0.12;

      if (cursorRef.current) {
        cursorRef.current.style.transform = `translate(${pos.current.x}px, ${pos.current.y}px)`;
      }
      if (dotRef.current) {
        dotRef.current.style.transform = `translate(${target.current.x}px, ${target.current.y}px)`;
      }
      raf.current = requestAnimationFrame(loop);
    };
    raf.current = requestAnimationFrame(loop);

    return () => {
      window.removeEventListener("mousemove", onMove);
      observer.disconnect();
      cancelAnimationFrame(raf.current);
    };
  }, []);

  return (
    <>
      <div ref={cursorRef} className="cursor-ring" />
      <div ref={dotRef} className="cursor-dot" />
    </>
  );
}

function MagneticElement({ children, strength = 0.35, className = "", style = {} }) {
  const ref = React.useRef(null);
  const animRef = React.useRef(null);
  const current = React.useRef({ x: 0, y: 0 });

  const handleMouseMove = (e) => {
    const el = ref.current;
    if (!el) return;
    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 tx = dx * strength;
    const ty = dy * strength;

    cancelAnimationFrame(animRef.current);
    const animate = () => {
      current.current.x += (tx - current.current.x) * 0.15;
      current.current.y += (ty - current.current.y) * 0.15;
      if (el) el.style.transform = `translate(${current.current.x}px, ${current.current.y}px)`;
      if (Math.abs(tx - current.current.x) > 0.1 || Math.abs(ty - current.current.y) > 0.1) {
        animRef.current = requestAnimationFrame(animate);
      }
    };
    animRef.current = requestAnimationFrame(animate);
  };

  const handleMouseLeave = () => {
    cancelAnimationFrame(animRef.current);
    const el = ref.current;
    const release = () => {
      current.current.x += (0 - current.current.x) * 0.12;
      current.current.y += (0 - current.current.y) * 0.12;
      if (el) el.style.transform = `translate(${current.current.x}px, ${current.current.y}px)`;
      if (Math.abs(current.current.x) > 0.05 || Math.abs(current.current.y) > 0.05) {
        animRef.current = requestAnimationFrame(release);
      } else {
        if (el) el.style.transform = `translate(0px, 0px)`;
      }
    };
    animRef.current = requestAnimationFrame(release);
  };

  return (
    <div
      ref={ref}
      className={`magnetic-wrap ${className}`}
      style={style}
      onMouseMove={handleMouseMove}
      onMouseLeave={handleMouseLeave}
    >
      {children}
    </div>
  );
}

function RevealText({ children, delay = 0, className = "", as: Tag = "div" }) {
  const ref = React.useRef(null);
  const [visible, setVisible] = React.useState(false);

  React.useEffect(() => {
    const el = ref.current;
    if (!el) return;
    const observer = new IntersectionObserver(
      ([entry]) => {
        if (entry.isIntersecting) {
          setTimeout(() => setVisible(true), delay);
          observer.disconnect();
        }
      },
      { threshold: 0.1 }
    );
    observer.observe(el);
    return () => observer.disconnect();
  }, [delay]);

  return (
    <Tag
      ref={ref}
      className={`reveal-text ${visible ? "revealed" : ""} ${className}`}
    >
      {children}
    </Tag>
  );
}

function RevealWords({ children, delay = 0, className = "", stagger = 60 }) {
  const ref = React.useRef(null);
  const [visible, setVisible] = React.useState(false);

  React.useEffect(() => {
    const el = ref.current;
    if (!el) return;
    const observer = new IntersectionObserver(
      ([entry]) => {
        if (entry.isIntersecting) {
          setTimeout(() => setVisible(true), delay);
          observer.disconnect();
        }
      },
      { threshold: 0.05 }
    );
    observer.observe(el);
    return () => observer.disconnect();
  }, [delay]);

  const words = typeof children === "string" ? children.split(" ") : [children];

  return (
    <div ref={ref} className={`reveal-words-container ${className}`} aria-label={typeof children === "string" ? children : undefined}>
      {words.map((word, i) => (
        <span
          key={i}
          className="word-wrap"
          style={{ display: "inline-block", overflow: "hidden", marginRight: "0.25em" }}
        >
          <span
            className="word-inner"
            style={{
              display: "inline-block",
              transform: visible ? "translateY(0)" : "translateY(110%)",
              opacity: visible ? 1 : 0,
              transition: `transform 0.75s cubic-bezier(0.16, 1, 0.3, 1) ${delay + i * stagger}ms, opacity 0.5s ease ${delay + i * stagger}ms`,
            }}
          >
            {word}
          </span>
        </span>
      ))}
    </div>
  );
}

Object.assign(window, { CustomCursor, MagneticElement, RevealText, RevealWords });
