/**
 * useIconPreload Hook
 *
 * Preloads icon images for smooth rendering without flash.
 * Used in constructor.tsx to preload navigation/tooltip/description icons.
 */

import { useState, useEffect, useRef, useCallback, useMemo } from 'react';
import { resolveAssetPlaybackUrl } from '../lib/assetUrl';
import { logger } from '../lib/logger';

interface UseIconPreloadOptions {
  /** Array of icon URLs to preload */
  iconUrls: string[];
  /** Whether preloading is enabled */
  enabled?: boolean;
}

interface UseIconPreloadResult {
  /** Map of URL to preload status (true if ready) */
  preloadedUrlMap: Record<string, boolean>;
  /** Check if a specific URL is preloaded */
  isPreloaded: (url: string) => boolean;
  /** Number of icons currently being preloaded */
  pendingCount: number;
  /** Whether all icons are preloaded */
  allPreloaded: boolean;
}

/**
 * Hook for preloading icon images.
 * Prevents flash when icons are first rendered by pre-decoding them.
 *
 * @example
 * const iconUrls = elements
 *   .filter(el => el.iconUrl)
 *   .map(el => resolveAssetPlaybackUrl(el.iconUrl));
 *
 * const { isPreloaded } = useIconPreload({ iconUrls });
 *
 * // Only render element if icon is ready
 * if (element.iconUrl && !isPreloaded(element.iconUrl)) return null;
 */
export function useIconPreload({
  iconUrls,
  enabled = true,
}: UseIconPreloadOptions): UseIconPreloadResult {
  const [preloadedUrlMap, setPreloadedUrlMap] = useState<
    Record<string, boolean>
  >({});
  const preloadedUrlsRef = useRef<Set<string>>(new Set());

  // Clean up preloaded set when target URLs change
  useEffect(() => {
    if (typeof window === 'undefined') return;

    const targetSet = new Set(iconUrls);
    const nextPreloaded = new Set<string>();

    // Keep only URLs that are still in the target list
    preloadedUrlsRef.current.forEach((url) => {
      if (targetSet.has(url)) {
        nextPreloaded.add(url);
      }
    });

    preloadedUrlsRef.current = nextPreloaded;

    // Update state map
    setPreloadedUrlMap(() => {
      const nextMap: Record<string, boolean> = {};
      nextPreloaded.forEach((url) => {
        nextMap[url] = true;
      });
      return nextMap;
    });
  }, [iconUrls]);

  // Preload icons
  useEffect(() => {
    if (typeof window === 'undefined') return;
    if (!enabled || !iconUrls.length) return;

    let isCancelled = false;
    const preloadImages: HTMLImageElement[] = [];

    iconUrls.forEach((url) => {
      // Skip if already preloaded
      if (preloadedUrlsRef.current.has(url)) return;

      const image = new Image();

      const markReady = () => {
        if (isCancelled) return;
        preloadedUrlsRef.current.add(url);
        setPreloadedUrlMap((prev) => {
          if (prev[url]) return prev;
          return { ...prev, [url]: true };
        });
      };

      image.onload = markReady;
      image.onerror = () => {
        logger.error('Failed to preload icon asset:', { url });
        markReady(); // Mark as ready anyway to avoid blocking
      };

      image.src = url;
      preloadImages.push(image);
    });

    return () => {
      isCancelled = true;
      preloadImages.forEach((image) => {
        image.onload = null;
        image.onerror = null;
      });
    };
  }, [iconUrls, enabled]);

  const isPreloaded = useCallback(
    (url: string): boolean => {
      return Boolean(preloadedUrlMap[url]);
    },
    [preloadedUrlMap],
  );

  const pendingCount = useMemo(() => {
    return iconUrls.filter((url) => !preloadedUrlMap[url]).length;
  }, [iconUrls, preloadedUrlMap]);

  const allPreloaded = useMemo(() => {
    return iconUrls.every((url) => preloadedUrlMap[url]);
  }, [iconUrls, preloadedUrlMap]);

  return {
    preloadedUrlMap,
    isPreloaded,
    pendingCount,
    allPreloaded,
  };
}

/**
 * Build icon preload targets from elements.
 * Utility to extract icon URLs that need preloading.
 */
export function buildIconPreloadTargets(
  elements: Array<{ type: string; iconUrl?: string }>,
  typeCheckers: {
    isNavigationElementType: (type: string) => boolean;
    isTooltipElementType: (type: string) => boolean;
    isDescriptionElementType: (type: string) => boolean;
  },
): string[] {
  const {
    isNavigationElementType,
    isTooltipElementType,
    isDescriptionElementType,
  } = typeCheckers;

  const urls = elements
    .filter(
      (element) =>
        (isNavigationElementType(element.type) ||
          isTooltipElementType(element.type) ||
          isDescriptionElementType(element.type)) &&
        Boolean(element.iconUrl),
    )
    .map((element) => resolveAssetPlaybackUrl(element.iconUrl))
    .filter(Boolean) as string[];

  return Array.from(new Set(urls));
}

export default useIconPreload;
