const LOOP_VIDEO_PATH = "uploads/character-glitch-loop-lite.mp4";
const LOOP_VIDEO_POSTER_PATH = "uploads/character-glitch-loop-poster.webp";
const ENABLE_LOOP_VIDEO = false;

function shouldSkipLoopVideo() {
  if (!ENABLE_LOOP_VIDEO) return true;
  const connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection;
  const saveData = Boolean(connection && connection.saveData);
  const reduceMotion = Boolean(window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches);
  const compactViewport = Boolean(window.matchMedia && window.matchMedia("(max-width: 1024px)").matches);
  return saveData || reduceMotion || compactViewport;
}

function LoopVideoPanel({ containerClassName, videoClassName, shouldLoad = true }) {
  const videoRef = React.useRef(null);
  const [isReady, setIsReady] = React.useState(false);

  React.useEffect(() => {
    const video = videoRef.current;
    if (!video || !shouldLoad) return;

    video.muted = true;
    video.defaultMuted = true;
    video.playsInline = true;

    const attemptPlay = () => {
      const playPromise = video.play();
      if (playPromise && typeof playPromise.catch === "function") {
        playPromise.catch(() => {});
      }
    };

    const handleCanPlay = () => {
      setIsReady(true);
      attemptPlay();
    };

    video.addEventListener("canplay", handleCanPlay);
    attemptPlay();

    return () => {
      video.removeEventListener("canplay", handleCanPlay);
    };
  }, [shouldLoad]);

  return (
    <div className={`${containerClassName}${isReady ? " is-ready" : ""}`} aria-hidden="true">
      <video
        ref={videoRef}
        className={videoClassName}
        src={shouldLoad ? LOOP_VIDEO_PATH : undefined}
        autoPlay
        loop
        muted
        playsInline
        preload={shouldLoad ? "metadata" : "none"}
        poster={LOOP_VIDEO_POSTER_PATH}
      />
    </div>
  );
}

function StickyLoop({ shouldLoad }) {
  return <LoopVideoPanel containerClassName="loop-sticky-inner" videoClassName="loop-video" shouldLoad={shouldLoad}/>;
}

function MobileLoopMedia() {
  return <LoopVideoPanel containerClassName="loop-stage-mobile-media" videoClassName="loop-video-mobile"/>;
}

function LoopStage({ children }) {
  const stageRef = React.useRef(null);
  const animRef = React.useRef(null);
  const [shouldLoadVideo, setShouldLoadVideo] = React.useState(false);

  React.useEffect(() => {
    const stage = stageRef.current;
    if (!stage) return;

    if (shouldSkipLoopVideo()) return;

    if (!("IntersectionObserver" in window)) {
      setShouldLoadVideo(true);
      return;
    }

    const observer = new IntersectionObserver((entries) => {
      if (!entries.some((entry) => entry.isIntersecting)) return;
      setShouldLoadVideo(true);
      observer.disconnect();
    }, { rootMargin: "240px 0px" });

    observer.observe(stage);

    return () => {
      observer.disconnect();
    };
  }, []);

  React.useEffect(() => {
    const stage = stageRef.current;
    const anim = animRef.current;
    if (!stage || !anim) return;

    let rafId = 0;
    let queued = false;

    const clamp = (value, min = 0, max = 1) => Math.min(max, Math.max(min, value));

    const updateAnimOpacity = () => {
      queued = false;
      const stageRect = stage.getBoundingClientRect();
      const viewportHeight = window.innerHeight || 1;
      const isActive = stageRect.top < viewportHeight && stageRect.bottom > 0;

      if (isActive) anim.classList.add("is-on");
      else anim.classList.remove("is-on");

      if (!isActive) {
        anim.style.setProperty("--loop-anim-opacity", "0");
        anim.style.setProperty("--loop-anim-reveal", "0");
        return;
      }

      const fadeMarker = stage.querySelector("[data-loop-fade-start]");
      let opacity = 1;

      if (fadeMarker) {
        const markerRect = fadeMarker.getBoundingClientRect();
        const fadeStart = viewportHeight * 0.86;
        const fadeEnd = viewportHeight * 0.18;
        opacity = clamp(
          (markerRect.top - fadeEnd) / Math.max(1, fadeStart - fadeEnd)
        );
      }

      const revealStart = viewportHeight;
      const revealEnd = viewportHeight * 0.36;
      const reveal = clamp(
        (revealStart - stageRect.top) / Math.max(1, revealStart - revealEnd)
      );

      anim.style.setProperty("--loop-anim-opacity", opacity.toFixed(3));
      anim.style.setProperty("--loop-anim-reveal", reveal.toFixed(3));
    };

    const scheduleOpacityUpdate = () => {
      if (queued) return;
      queued = true;
      rafId = requestAnimationFrame(updateAnimOpacity);
    };

    window.addEventListener("scroll", scheduleOpacityUpdate, { passive: true });
    window.addEventListener("resize", scheduleOpacityUpdate);
    scheduleOpacityUpdate();

    return () => {
      window.removeEventListener("scroll", scheduleOpacityUpdate);
      window.removeEventListener("resize", scheduleOpacityUpdate);
      if (rafId) cancelAnimationFrame(rafId);
    };
  }, []);

  return (
    <div ref={stageRef} className="loop-stage loop-stage-slice-underlay">
      <div className="loop-stage-content">
        <div ref={animRef} className="loop-stage-anim" aria-hidden="true">
          <StickyLoop shouldLoad={shouldLoadVideo}/>
        </div>
        {children}
      </div>
    </div>
  );
}

window.StickyLoop = StickyLoop;
window.LoopStage = LoopStage;
