/* global React */
const { useEffect, useState, useRef } = React;

// =============================================================
// useScrollY: current window scroll
// =============================================================
function useScrollY() {
  const [y, setY] = useState(0);
  useEffect(() => {
    let raf;
    const onScroll = () => {
      cancelAnimationFrame(raf);
      raf = requestAnimationFrame(() => setY(window.scrollY));
    };
    window.addEventListener("scroll", onScroll, { passive: true });
    onScroll();
    return () => { window.removeEventListener("scroll", onScroll); cancelAnimationFrame(raf); };
  }, []);
  return y;
}

// =============================================================
// useElementProgress: 0 when element enters viewport bottom, 1 when it leaves the top
// =============================================================
function useElementProgress(ref) {
  const [p, setP] = useState(0);
  useEffect(() => {
    let raf;
    const update = () => {
      if (!ref.current) return;
      const r = ref.current.getBoundingClientRect();
      const vh = window.innerHeight;
      const total = r.height + vh;
      const seen = vh - r.top;
      setP(Math.max(0, Math.min(1, seen / total)));
    };
    const onScroll = () => { cancelAnimationFrame(raf); raf = requestAnimationFrame(update); };
    window.addEventListener("scroll", onScroll, { passive: true });
    window.addEventListener("resize", onScroll);
    update();
    return () => { window.removeEventListener("scroll", onScroll); window.removeEventListener("resize", onScroll); cancelAnimationFrame(raf); };
  }, [ref]);
  return p;
}

// =============================================================
// useMouse: normalized mouse pos relative to element
// =============================================================
function useMouse(ref) {
  const [m, setM] = useState({ x: 0.5, y: 0.5, active: false });
  useEffect(() => {
    if (!ref.current) return;
    const el = ref.current;
    const onMove = (e) => {
      const r = el.getBoundingClientRect();
      setM({
        x: (e.clientX - r.left) / r.width,
        y: (e.clientY - r.top) / r.height,
        active: true,
      });
    };
    const onLeave = () => setM((p) => ({ ...p, active: false }));
    el.addEventListener("mousemove", onMove);
    el.addEventListener("mouseleave", onLeave);
    return () => { el.removeEventListener("mousemove", onMove); el.removeEventListener("mouseleave", onLeave); };
  }, [ref]);
  return m;
}

// =============================================================
// useGlobalMouse: viewport-normalized mouse
// =============================================================
function useGlobalMouse() {
  const [m, setM] = useState({ x: 0.5, y: 0.5 });
  useEffect(() => {
    const onMove = (e) => setM({ x: e.clientX / window.innerWidth, y: e.clientY / window.innerHeight });
    window.addEventListener("mousemove", onMove);
    return () => window.removeEventListener("mousemove", onMove);
  }, []);
  return m;
}

// =============================================================
// CursorGlow: soft moving spotlight that follows the mouse
// =============================================================
function CursorGlow({ color = "rgba(56,189,248,0.18)", size = 600 }) {
  const { x, y } = useGlobalMouse();
  const [isLight, setIsLight] = useState(false);
  // Touch devices have no real cursor: the glow would either sit static or
  // flash at the last touch point. Either way it's a wasted full-viewport
  // repaint on every "mousemove". Disable it on coarse pointers / mobile.
  const [enabled, setEnabled] = useState(true);
  useEffect(() => {
    if (typeof window === "undefined") return;
    const mq = window.matchMedia("(pointer: coarse), (max-width: 760px), (prefers-reduced-motion: reduce)");
    const apply = () => setEnabled(!mq.matches);
    apply();
    if (mq.addEventListener) mq.addEventListener("change", apply);
    else mq.addListener(apply);
    return () => {
      if (mq.removeEventListener) mq.removeEventListener("change", apply);
      else mq.removeListener(apply);
    };
  }, []);
  useEffect(() => {
    const check = () => setIsLight(document.documentElement.dataset.theme === "light");
    check();
    const obs = new MutationObserver(check);
    obs.observe(document.documentElement, { attributes: true, attributeFilter: ["data-theme"] });
    return () => obs.disconnect();
  }, []);
  if (!enabled) return null;
  return (
    <div style={{
      position: "fixed", pointerEvents: "none", zIndex: 1,
      left: 0, top: 0, width: "100%", height: "100%",
      background: `radial-gradient(${size}px circle at ${x * 100}% ${y * 100}%, ${color}, transparent 60%)`,
      transition: "background 100ms linear",
      mixBlendMode: isLight ? "multiply" : "screen",
    }}/>
  );
}

// =============================================================
// Magnetic: element pulls toward mouse on hover
// =============================================================
function Magnetic({ children, strength = 0.25, style = {}, ...rest }) {
  const ref = useRef();
  const [t, setT] = useState({ x: 0, y: 0 });
  useEffect(() => {
    if (!ref.current) return;
    const el = ref.current;
    const onMove = (e) => {
      const r = el.getBoundingClientRect();
      const cx = r.left + r.width / 2, cy = r.top + r.height / 2;
      setT({ x: (e.clientX - cx) * strength, y: (e.clientY - cy) * strength });
    };
    const onLeave = () => setT({ x: 0, y: 0 });
    el.addEventListener("mousemove", onMove);
    el.addEventListener("mouseleave", onLeave);
    return () => { el.removeEventListener("mousemove", onMove); el.removeEventListener("mouseleave", onLeave); };
  }, [strength]);
  return (
    <span ref={ref} style={{ display: "inline-block", transform: `translate(${t.x}px, ${t.y}px)`, transition: "transform 200ms cubic-bezier(0.2,0.8,0.2,1)", ...style }} {...rest}>{children}</span>
  );
}

// =============================================================
// Tilt: 3D tilt on mouse hover
// =============================================================
function Tilt({ children, max = 6, style = {}, perspective = 1200 }) {
  const ref = useRef();
  const m = useMouse(ref);
  const rx = (0.5 - m.y) * max * 2 * (m.active ? 1 : 0);
  const ry = (m.x - 0.5) * max * 2 * (m.active ? 1 : 0);
  return (
    <div ref={ref} style={{ perspective: `${perspective}px`, transformStyle: "preserve-3d", ...style }}>
      <div style={{
        transform: `rotateX(${rx}deg) rotateY(${ry}deg)`,
        transition: "transform 200ms cubic-bezier(0.2,0.8,0.2,1)",
        transformStyle: "preserve-3d",
      }}>{children}</div>
    </div>
  );
}

// =============================================================
// SplitText: words appear individually on reveal
// =============================================================
function SplitText({ text, delay = 0, style = {}, gap = "0.18em", className = "" }) {
  const ref = useRef();
  const [shown, setShown] = useState(false);
  useEffect(() => {
    const io = new IntersectionObserver((es) => {
      if (es[0].isIntersecting) { setShown(true); io.disconnect(); }
    }, { threshold: 0.3 });
    if (ref.current) io.observe(ref.current);
    return () => io.disconnect();
  }, []);
  const words = text.split(" ");
  return (
    <span ref={ref} className={className} style={style}>
      {words.map((w, i) => (
        <span key={i} style={{
          display: "inline-block", marginRight: gap,
          opacity: shown ? 1 : 0,
          transform: shown ? "translateY(0)" : "translateY(0.4em)",
          transition: `opacity 700ms cubic-bezier(0.2,0.8,0.2,1) ${delay + i * 60}ms, transform 700ms cubic-bezier(0.2,0.8,0.2,1) ${delay + i * 60}ms`,
        }}>{w}</span>
      ))}
    </span>
  );
}

// =============================================================
// Parallax: translateY based on scroll progress
// =============================================================
function Parallax({ children, speed = 0.2, style = {} }) {
  const ref = useRef();
  const p = useElementProgress(ref);
  return (
    <div ref={ref} style={{ ...style }}>
      <div style={{ transform: `translate3d(0, ${(p - 0.5) * speed * 200}px, 0)`, willChange: "transform" }}>{children}</div>
    </div>
  );
}

// =============================================================
// ThemeToggle context
// =============================================================
const ThemeContext = React.createContext({ theme: "dark", toggle: () => {} });
function ThemeProvider({ children }) {
  const getSystem = () =>
    typeof window !== "undefined" && window.matchMedia("(prefers-color-scheme: light)").matches
      ? "light" : "dark";
  const [theme, setTheme] = useState(() => {
    if (typeof localStorage === "undefined") return "dark";
    return localStorage.getItem("dimmy-theme") || getSystem();
  });
  useEffect(() => {
    document.documentElement.dataset.theme = theme;
  }, [theme]);
  // Follow system theme when user has no manual override
  useEffect(() => {
    if (typeof window === "undefined") return;
    const mq = window.matchMedia("(prefers-color-scheme: light)");
    const onChange = (e) => {
      if (!localStorage.getItem("dimmy-theme")) setTheme(e.matches ? "light" : "dark");
    };
    if (mq.addEventListener) mq.addEventListener("change", onChange);
    else mq.addListener(onChange);
    return () => {
      if (mq.removeEventListener) mq.removeEventListener("change", onChange);
      else mq.removeListener(onChange);
    };
  }, []);
  const toggle = () => setTheme((t) => {
    const next = t === "dark" ? "light" : "dark";
    if (typeof localStorage !== "undefined") localStorage.setItem("dimmy-theme", next);
    return next;
  });
  return <ThemeContext.Provider value={{ theme, toggle }}>{children}</ThemeContext.Provider>;
}
function useTheme() { return React.useContext(ThemeContext); }

window.Motion = { useScrollY, useElementProgress, useMouse, useGlobalMouse, CursorGlow, Magnetic, Tilt, SplitText, Parallax, ThemeProvider, useTheme };
