import { type ReactNode, useEffect, useRef, useState } from 'react';

import {
  collectRouteImageUrls,
  hasCssImageUrlChange,
  type ImagePreloadResult,
} from './routeImageReadyGateUtils';
import { RouteLoadingScreen } from './RouteLoadingScreen';

type RouteImageReadyGateProps = {
  children: ReactNode;
  eyebrow: string;
  text: string;
};

const IMAGE_GATE_QUIET_MS = 140;
const IMAGE_GATE_MIN_VISIBLE_WAIT_MS = 260;
const IMAGE_GATE_MAX_BLOCK_MS = 1400;
const IMAGE_PRELOAD_TIMEOUT_MS = 12000;

const settledImageUrls = new Set<string>();
const imagePreloadTasks = new Map<string, Promise<ImagePreloadResult>>();

function preloadImageUrl(url: string) {
  const existingTask = imagePreloadTasks.get(url);
  if (existingTask) {
    return existingTask;
  }

  const task = new Promise<ImagePreloadResult>((resolve) => {
    if (typeof Image === 'undefined') {
      resolve({ url, status: 'loaded' });
      return;
    }

    const image = new Image();
    let settled = false;

    const settle = (status: ImagePreloadResult['status']) => {
      if (settled) {
        return;
      }

      settled = true;
      window.clearTimeout(timeoutId);
      settledImageUrls.add(url);
      resolve({ url, status });
    };

    const timeoutId = window.setTimeout(() => {
      settle('timeout');
    }, IMAGE_PRELOAD_TIMEOUT_MS);

    image.onload = () => settle('loaded');
    image.onerror = () => settle('failed');
    image.decoding = 'async';
    image.src = url;

    if (image.complete) {
      settle(image.naturalWidth > 0 ? 'loaded' : 'failed');
    }
  });

  imagePreloadTasks.set(url, task);
  return task;
}

/**
 * 路由首屏图片门闩：业务页面先真实挂载但不可见，
 * 只等待短暂稳定窗口，不再把所有图片加载完成作为首屏硬阻塞。
 */
export function RouteImageReadyGate({
  children,
  eyebrow,
  text,
}: RouteImageReadyGateProps) {
  const rootRef = useRef<HTMLDivElement | null>(null);
  const startTimeRef = useRef(0);
  const scanTimerRef = useRef<number | null>(null);
  const revealTimerRef = useRef<number | null>(null);
  const scanVersionRef = useRef(0);
  const revealedRef = useRef(false);
  const [ready, setReady] = useState(false);

  useEffect(() => {
    const root = rootRef.current;
    if (!root) {
      return;
    }

    let disposed = false;
    startTimeRef.current = window.performance.now();
    revealedRef.current = false;
    setReady(false);

    const clearScanTimer = () => {
      if (scanTimerRef.current !== null) {
        window.clearTimeout(scanTimerRef.current);
        scanTimerRef.current = null;
      }
    };

    const clearRevealTimer = () => {
      if (revealTimerRef.current !== null) {
        window.clearTimeout(revealTimerRef.current);
        revealTimerRef.current = null;
      }
    };

    const scheduleScan = () => {
      clearScanTimer();
      scanTimerRef.current = window.setTimeout(runScan, IMAGE_GATE_QUIET_MS);
    };

    const scheduleReveal = (version: number) => {
      if (revealedRef.current) {
        return;
      }

      clearRevealTimer();

      const elapsed = window.performance.now() - startTimeRef.current;
      const preferredDelay = Math.max(
        IMAGE_GATE_QUIET_MS,
        IMAGE_GATE_MIN_VISIBLE_WAIT_MS - elapsed,
      );
      const maxRemainingDelay = Math.max(0, IMAGE_GATE_MAX_BLOCK_MS - elapsed);
      const delay = Math.min(preferredDelay, maxRemainingDelay);

      revealTimerRef.current = window.setTimeout(() => {
        if (disposed || version !== scanVersionRef.current) {
          return;
        }

        revealedRef.current = true;
        setReady(true);
      }, delay);
    };

    function runScan() {
      if (disposed) {
        return;
      }

      const version = scanVersionRef.current + 1;
      scanVersionRef.current = version;
      const pendingUrls = collectRouteImageUrls(root).filter(
        (url) => !settledImageUrls.has(url),
      );

      if (pendingUrls.length > 0) {
        // 首屏慢加载的核心约束：图片可预热，但不能无限期阻塞页面主体可见。
        pendingUrls.forEach((url) => {
          void preloadImageUrl(url);
        });
      }

      scheduleReveal(version);
    }

    const observer = new MutationObserver((mutations) => {
      if (disposed || revealedRef.current) {
        return;
      }

      const shouldRescan = mutations.some((mutation) => {
        if (mutation.type === 'childList') {
          return (
            mutation.addedNodes.length > 0 ||
            mutation.removedNodes.length > 0
          );
        }
        if (mutation.type !== 'attributes') {
          return false;
        }
        if (
          mutation.attributeName === 'src' ||
          mutation.attributeName === 'srcset'
        ) {
          return true;
        }
        return mutation.attributeName === 'style'
          ? hasCssImageUrlChange(mutation)
          : false;
      });

      if (!shouldRescan) {
        return;
      }

      clearRevealTimer();
      scheduleScan();
    });

    observer.observe(root, {
      attributes: true,
      attributeFilter: ['src', 'srcset', 'style'],
      attributeOldValue: true,
      childList: true,
      subtree: true,
    });
    scheduleScan();

    return () => {
      disposed = true;
      observer.disconnect();
      clearScanTimer();
      clearRevealTimer();
    };
  }, []);

  return (
    <>
      <div
        ref={rootRef}
        aria-hidden={!ready}
        style={{
          visibility: ready ? 'visible' : 'hidden',
        }}
      >
        {children}
      </div>
      {!ready ? (
        <div className="fixed inset-0 z-[9999]">
          <RouteLoadingScreen eyebrow={eyebrow} text={text} />
        </div>
      ) : null}
    </>
  );
}
