/* 1:1 — Landing motion
   Editorial cursor, sage spotlight, parallax tilt, hairline draws,
   scroll progress, reveal-on-scroll, Brisbane time clock. */

const { useEffect, useRef, useState } = React;

const MotionContext = React.createContext({ level: "full" });

// ============================================================
// useReducedMotion — system pref
// ============================================================
function useReducedMotion() {
  const [r, setR] = useState(false);
  useEffect(() => {
    const m = window.matchMedia("(prefers-reduced-motion: reduce)");
    const fn = () => setR(m.matches);
    fn();
    m.addEventListener?.("change", fn);
    return () => m.removeEventListener?.("change", fn);
  }, []);
  return r;
}
function useIsTouch() {
  const [t, setT] = useState(false);
  useEffect(() => {
    setT(window.matchMedia?.("(pointer: coarse)")?.matches || false);
  }, []);
  return t;
}

// ============================================================
// EditorialCursor — sage dot, contextual
// ============================================================
function EditorialCursor() {
  const { level } = React.useContext(MotionContext);
  const reduced = useReducedMotion();
  const touch = useIsTouch();
  const ref = useRef(null);
  const target = useRef({ x: -50, y: -50 });
  const pos = useRef({ x: -50, y: -50 });
  const stateRef = useRef("default");

  useEffect(() => {
    if (reduced || touch || level === "off") return;
    const cur = ref.current;
    if (!cur) return;
    let raf = 0;

    function tick() {
      const lag = 0.18;
      pos.current.x += (target.current.x - pos.current.x) * lag;
      pos.current.y += (target.current.y - pos.current.y) * lag;
      cur.style.transform =
        `translate3d(${pos.current.x}px, ${pos.current.y}px, 0) translate(-50%, -50%)`;
      raf = requestAnimationFrame(tick);
    }
    raf = requestAnimationFrame(tick);

    function onMove(e) {
      target.current.x = e.clientX;
      target.current.y = e.clientY;
      const el = e.target;
      let next = "default";
      if (el && el.closest) {
        if (el.closest("[data-cursor='cta'], .store-badge, .lp-nav-cta, .waitlist-row button")) {
          next = "cta";
        } else if (el.closest("a, button, [data-cursor='link'], .anchor-card, .phone-wrap")) {
          next = "link";
        } else if (
          el.tagName === "INPUT" ||
          el.tagName === "TEXTAREA" ||
          el.closest("p, h1, h2, h3, h4, .lp-lede, .lp-body, .lp-step-body, .lp-isnt-cell p, [data-cursor='text']")
        ) {
          next = "text";
        }
      }
      if (next !== stateRef.current) {
        stateRef.current = next;
        cur.dataset.state = next;
      }
    }
    function onLeave() {
      target.current.x = -50;
      target.current.y = -50;
    }
    window.addEventListener("mousemove", onMove);
    window.addEventListener("mouseleave", onLeave);
    document.documentElement.style.cursor = "none";
    return () => {
      window.removeEventListener("mousemove", onMove);
      window.removeEventListener("mouseleave", onLeave);
      cancelAnimationFrame(raf);
      document.documentElement.style.cursor = "";
    };
  }, [reduced, touch, level]);

  if (reduced || touch || level === "off") return null;
  return <div ref={ref} className="lp-cursor" data-state="default"></div>;
}

// ============================================================
// SageSpotlight — soft radial glow chasing the cursor in hero
// ============================================================
function SageSpotlight({ scopeRef }) {
  const { level } = React.useContext(MotionContext);
  const reduced = useReducedMotion();
  const touch = useIsTouch();
  const ref = useRef(null);
  const target = useRef({ x: 0.5, y: 0.5 });
  const pos = useRef({ x: 0.5, y: 0.5 });

  useEffect(() => {
    if (reduced || touch || level === "off") return;
    const el = ref.current;
    const scope = scopeRef?.current;
    if (!el || !scope) return;
    let raf = 0;
    let on = false;

    function tick() {
      const lag = 0.05; // very slow
      pos.current.x += (target.current.x - pos.current.x) * lag;
      pos.current.y += (target.current.y - pos.current.y) * lag;
      el.style.background = `radial-gradient(600px circle at ${pos.current.x * 100}% ${pos.current.y * 100}%, rgba(143, 179, 122, 0.10), rgba(143, 179, 122, 0) 60%)`;
      raf = requestAnimationFrame(tick);
    }
    raf = requestAnimationFrame(tick);

    function onMove(e) {
      const rect = scope.getBoundingClientRect();
      target.current.x = (e.clientX - rect.left) / rect.width;
      target.current.y = (e.clientY - rect.top) / rect.height;
      if (!on) {
        on = true;
        el.dataset.on = "true";
      }
    }
    function onLeave() {
      on = false;
      el.dataset.on = "false";
    }
    scope.addEventListener("mousemove", onMove);
    scope.addEventListener("mouseleave", onLeave);
    return () => {
      scope.removeEventListener("mousemove", onMove);
      scope.removeEventListener("mouseleave", onLeave);
      cancelAnimationFrame(raf);
    };
  }, [reduced, touch, level, scopeRef]);

  return <div ref={ref} className="lp-hero-spotlight" data-on="false"></div>;
}

// ============================================================
// ParallaxTilt — wrapper that tilts to cursor
// ============================================================
function ParallaxTilt({ children, max = 4, className = "", style }) {
  const { level } = React.useContext(MotionContext);
  const reduced = useReducedMotion();
  const touch = useIsTouch();
  const ref = useRef(null);
  const target = useRef({ rx: 0, ry: 0, ty: 0 });
  const pos = useRef({ rx: 0, ry: 0, ty: 0 });

  useEffect(() => {
    if (reduced || touch || level === "off") return;
    const el = ref.current;
    if (!el) return;
    let raf = 0;
    function tick() {
      const lag = 0.12;
      pos.current.rx += (target.current.rx - pos.current.rx) * lag;
      pos.current.ry += (target.current.ry - pos.current.ry) * lag;
      pos.current.ty += (target.current.ty - pos.current.ty) * lag;
      el.style.transform =
        `perspective(1200px) rotateX(${pos.current.rx}deg) rotateY(${pos.current.ry}deg) translateY(${pos.current.ty}px)`;
      raf = requestAnimationFrame(tick);
    }
    raf = requestAnimationFrame(tick);

    function onMove(e) {
      const w = window.innerWidth;
      const h = window.innerHeight;
      const dx = (e.clientX - w / 2) / (w / 2); // -1..1
      const dy = (e.clientY - h / 2) / (h / 2);
      target.current.ry = dx * max;
      target.current.rx = -dy * (max * 0.5);
    }
    function onScroll() {
      const r = el.getBoundingClientRect();
      const center = r.top + r.height / 2;
      const off = (window.innerHeight / 2 - center) / window.innerHeight;
      target.current.ty = -off * 18;
    }
    window.addEventListener("mousemove", onMove);
    window.addEventListener("scroll", onScroll, { passive: true });
    onScroll();
    return () => {
      window.removeEventListener("mousemove", onMove);
      window.removeEventListener("scroll", onScroll);
      cancelAnimationFrame(raf);
    };
  }, [reduced, touch, level, max]);

  return (
    <div ref={ref} className={`phone-wrap ${className}`} data-tilt={!reduced && !touch && level !== "off"} style={style}>
      {children}
    </div>
  );
}

// ============================================================
// Reveal — fade-rise on enter
// ============================================================
function Reveal({ children, delay = 0, as: Tag = "div", className = "", ...rest }) {
  const ref = useRef(null);
  const [shown, setShown] = useState(false);
  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    const io = new IntersectionObserver(
      ([entry]) => {
        if (entry.isIntersecting) {
          setShown(true);
          io.disconnect();
        }
      },
      { threshold: 0.15, rootMargin: "0px 0px -10% 0px" }
    );
    io.observe(el);
    return () => io.disconnect();
  }, []);
  return (
    <Tag
      ref={ref}
      className={`reveal ${className}`}
      data-shown={shown ? "true" : "false"}
      data-delay={delay}
      {...rest}
    >
      {children}
    </Tag>
  );
}

// ============================================================
// HairlinePath — draws on enter
// ============================================================
function HairlinePath({ d, viewBox = "0 0 100 100" }) {
  const { level } = React.useContext(MotionContext);
  const ref = useRef(null);
  useEffect(() => {
    const svg = ref.current;
    if (!svg) return;
    if (level === "off") {
      svg.classList.add("drawn");
      return;
    }
    const io = new IntersectionObserver(
      ([entry]) => {
        if (entry.isIntersecting) {
          svg.classList.add("drawn");
          io.disconnect();
        }
      },
      { threshold: 0.3 }
    );
    io.observe(svg);
    return () => io.disconnect();
  }, [level]);
  return (
    <svg ref={ref} className="lp-loop-svg" viewBox={viewBox} preserveAspectRatio="none">
      <path d={d} pathLength="1" />
    </svg>
  );
}

// ============================================================
// ScrollProgress — sage hairline at top
// ============================================================
function ScrollProgress() {
  const { level } = React.useContext(MotionContext);
  const reduced = useReducedMotion();
  const ref = useRef(null);
  useEffect(() => {
    if (level === "off") return;
    const el = ref.current;
    if (!el) return;
    let raf = 0;
    let last = performance.now();
    let idleTimer = 0;
    function update() {
      const max = document.documentElement.scrollHeight - window.innerHeight;
      const p = Math.max(0, Math.min(1, window.scrollY / max));
      el.style.width = `${p * 100}vw`;
      el.style.opacity = "1";
      clearTimeout(idleTimer);
      idleTimer = setTimeout(() => { el.style.opacity = "0"; }, 1800);
    }
    function onScroll() {
      cancelAnimationFrame(raf);
      raf = requestAnimationFrame(update);
    }
    update();
    window.addEventListener("scroll", onScroll, { passive: true });
    return () => {
      window.removeEventListener("scroll", onScroll);
      cancelAnimationFrame(raf);
      clearTimeout(idleTimer);
    };
  }, [level]);
  if (reduced) return null;
  return <div ref={ref} className="lp-progress"></div>;
}

// ============================================================
// BrisbaneClock — footer ticker
// ============================================================
function BrisbaneClock() {
  const [now, setNow] = useState(() => formatBris(new Date()));
  useEffect(() => {
    const id = setInterval(() => setNow(formatBris(new Date())), 1000);
    return () => clearInterval(id);
  }, []);
  return <span className="lp-footer-time">{now}</span>;
}
function formatBris(d) {
  const fmt = new Intl.DateTimeFormat("en-AU", {
    timeZone: "Australia/Brisbane",
    hour12: false,
    hour: "2-digit",
    minute: "2-digit",
    second: "2-digit",
  });
  return `${fmt.format(d)} AEST`;
}

// ============================================================
// NavScrollState — adds data-scrolled to nav
// ============================================================
function useNavScroll(ref) {
  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    const onScroll = () => {
      el.dataset.scrolled = window.scrollY > 12 ? "true" : "false";
    };
    onScroll();
    window.addEventListener("scroll", onScroll, { passive: true });
    return () => window.removeEventListener("scroll", onScroll);
  }, [ref]);
}

Object.assign(window, {
  MotionContext,
  EditorialCursor,
  SageSpotlight,
  ParallaxTilt,
  Reveal,
  HairlinePath,
  ScrollProgress,
  BrisbaneClock,
  useReducedMotion,
  useIsTouch,
  useNavScroll,
});
