"use client";

import Image, { type StaticImageData } from "next/image";
import { useEffect, useRef, useState } from "react";
import { cn } from "@/lib/cn";

export interface VideoSource {
  src: string;
  type: "video/webm" | "video/mp4";
}

export interface VideoVariant {
  /** Smallest viewport width (CSS px) this variant is chosen for. */
  minViewport: number;
  sources: VideoSource[];
}

interface AmbientVideoProps {
  poster: StaticImageData;
  /** Ordered from largest minViewport to smallest. */
  variants: VideoVariant[];
  sizes: string;
  className?: string;
  mediaClassName?: string;
  playbackRate?: number;
  eagerPoster?: boolean;
}

type NavigatorWithConnection = Navigator & { connection?: { saveData?: boolean } };

/**
 * Silent ambient loop. The poster renders on the server; the video attaches its sources
 * only after page load, when visible, and never under reduced motion or Save-Data.
 */
export function AmbientVideo({
  poster,
  variants,
  sizes,
  className,
  mediaClassName,
  playbackRate = 1,
  eagerPoster = false,
}: AmbientVideoProps) {
  const videoRef = useRef<HTMLVideoElement>(null);
  const visibleRef = useRef(false);
  const [sources, setSources] = useState<VideoSource[] | null>(null);
  const [playing, setPlaying] = useState(false);

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

    const reducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)");
    const saveData = (navigator as NavigatorWithConnection).connection?.saveData === true;
    if (reducedMotion.matches || saveData) return;

    let observer: IntersectionObserver | undefined;
    let idleHandle: number | undefined;
    let cancelled = false;

    const begin = () => {
      if (cancelled) return;
      const width = window.innerWidth;
      const variant = variants.find((item) => width >= item.minViewport) ?? variants[variants.length - 1];
      observer = new IntersectionObserver(
        ([entry]) => {
          visibleRef.current = entry.isIntersecting;
          if (entry.isIntersecting) {
            setSources((current) => current ?? variant.sources);
            if (video.readyState >= 2) void video.play().catch(() => undefined);
          } else {
            video.pause();
          }
        },
        { rootMargin: "120px 0px" },
      );
      observer.observe(video);
    };

    const schedule = () => {
      const idle = window.requestIdleCallback ?? ((cb: () => void) => window.setTimeout(cb, 250));
      idleHandle = idle(begin) as number;
    };

    if (document.readyState === "complete") schedule();
    else window.addEventListener("load", schedule, { once: true });

    const onPreferenceChange = () => {
      if (reducedMotion.matches) {
        video.pause();
        setPlaying(false);
      }
    };
    reducedMotion.addEventListener("change", onPreferenceChange);

    return () => {
      cancelled = true;
      observer?.disconnect();
      window.removeEventListener("load", schedule);
      reducedMotion.removeEventListener("change", onPreferenceChange);
      if (idleHandle !== undefined) (window.cancelIdleCallback ?? window.clearTimeout)(idleHandle);
    };
  }, [variants]);

  useEffect(() => {
    const video = videoRef.current;
    if (!video || !sources) return;
    video.load();
    video.playbackRate = playbackRate;
    if (visibleRef.current) void video.play().catch(() => undefined);
  }, [sources, playbackRate]);

  return (
    <div className={cn("absolute inset-0 overflow-hidden", className)} aria-hidden="true">
      <Image
        src={poster}
        alt=""
        fill
        sizes={sizes}
        placeholder="blur"
        loading={eagerPoster ? "eager" : "lazy"}
        className={cn("object-cover", mediaClassName)}
      />
      <video
        ref={videoRef}
        muted
        loop
        playsInline
        preload="none"
        disablePictureInPicture
        tabIndex={-1}
        onPlaying={() => setPlaying(true)}
        onLoadedMetadata={(event) => {
          event.currentTarget.playbackRate = playbackRate;
        }}
        className={cn(
          "absolute inset-0 h-full w-full object-cover transition-opacity duration-[1200ms] ease-out",
          playing ? "opacity-100" : "opacity-0",
          mediaClassName,
        )}
      >
        {sources?.map((source) => <source key={source.src} src={source.src} type={source.type} />)}
      </video>
    </div>
  );
}
