"use client";

import { usePathname } from "next/navigation";
import { useEffect, useRef, useState } from "react";

// Module-level cache so HomeAnimatedHero can access preloaded images
// This persists across component mounts/unmounts
export const heroFrameCache: Map<number, HTMLImageElement> = new Map();
export let heroPreloadStarted = false;
export let heroLastPreloadedIndex = 0;

interface HeroFramePreloaderProps {
  framePath?: string;
  frameCount?: number;
  frameFormat?: string;
  framePrefix?: string;
  framePadding?: number;
}

/**
 * Invisible component that preloads hero frames. Mount in HomeHero only.
 * Skips when pathname !== "/" to avoid loading during Next.js prefetch
 * (e.g. when viewing contact page, prefetch of / would otherwise load frames).
 */
export default function HeroFramePreloader({
  framePath = "/images/home/hero/sequence",
  frameCount = 336,
  frameFormat = "jpg",
  framePrefix = "frame_",
  framePadding = 4,
}: HeroFramePreloaderProps) {
  const pathname = usePathname();
  const hasStartedRef = useRef(false);
  const [isDesktopViewport, setIsDesktopViewport] = useState(() => {
    if (typeof window === "undefined") return false;
    return window.matchMedia("(min-width: 1280px)").matches;
  });

  useEffect(() => {
    const mediaQuery = window.matchMedia("(min-width: 1280px)");
    const updateViewportState = () => setIsDesktopViewport(mediaQuery.matches);
    updateViewportState();
    mediaQuery.addEventListener("change", updateViewportState);
    return () => {
      mediaQuery.removeEventListener("change", updateViewportState);
    };
  }, []);

  useEffect(() => {
    // Skip when not on homepage - prevents loading during prefetch from other pages
    if (pathname !== "/") return;
    // Skip on mobile/tablet where animated desktop hero is never shown.
    if (!isDesktopViewport) return;
    // Only start once, even if component remounts
    if (hasStartedRef.current || heroPreloadStarted) return;
    hasStartedRef.current = true;
    heroPreloadStarted = true;

    const initialBatchSize = 4; // Keep startup network burst minimal
    const backgroundBatchSize = 6;

    const loadFrame = (index: number): Promise<void> => {
      // Skip if already cached
      if (heroFrameCache.has(index)) {
        return Promise.resolve();
      }

      const fileIndex = index + 1; // 0-based to 1-based
      const paddedIndex = String(fileIndex).padStart(framePadding, "0");
      const img = new Image();
      img.crossOrigin = "anonymous";

      return new Promise<void>((resolve) => {
        img.onload = () => {
          heroFrameCache.set(index, img);
          if (index > heroLastPreloadedIndex) {
            heroLastPreloadedIndex = index;
          }
          resolve();
        };
        img.onerror = () => {
          // Still resolve to continue loading other frames
          resolve();
        };
        img.src = `${framePath}/${framePrefix}${paddedIndex}.${frameFormat}`;
      });
    };

    const preloadAll = async () => {
      // Phase 1: Load first batch quickly (parallel)
      const initialPromises: Promise<void>[] = [];
      for (let i = 0; i < Math.min(initialBatchSize, frameCount); i++) {
        initialPromises.push(loadFrame(i));
      }
      await Promise.all(initialPromises);

      // Phase 2: Load remaining frames in background batches
      const loadBatch = async (from: number) => {
        const end = Math.min(from + backgroundBatchSize, frameCount);
        const promises: Promise<void>[] = [];
        for (let i = from; i < end; i++) {
          promises.push(loadFrame(i));
        }
        await Promise.all(promises);

        if (end < frameCount) {
          // Use requestIdleCallback if available, else setTimeout
          const idleCallback: (cb: () => void) => void =
            typeof (
              window as Window & {
                requestIdleCallback?: (cb: () => void) => void;
              }
            ).requestIdleCallback === "function"
              ? (
                  window as Window & {
                    requestIdleCallback: (cb: () => void) => void;
                  }
                ).requestIdleCallback
              : (cb: () => void) => {
                  setTimeout(cb, 150);
                };

          idleCallback(() => {
            void loadBatch(end);
          });
        }
      };

      if (initialBatchSize < frameCount) {
        void loadBatch(initialBatchSize);
      }
    };

    void preloadAll();
  }, [pathname, isDesktopViewport, framePath, frameCount, frameFormat, framePrefix, framePadding]);

  // This component renders nothing visible
  return null;
}
