/**
 * Image Pre-Decode Utilities
 *
 * Utilities for pre-decoding images before displaying to prevent white flashes
 * during page transitions.
 */

import { resolveAssetPlaybackUrl } from './assetUrl';
import { PRELOAD_CONFIG } from '../config/preload.config';

/**
 * Minimal page interface for image extraction
 */
export interface PageWithImages {
  background_image_url?: string;
  ui_schema_json?: string | Record<string, unknown>;
}

/**
 * Optional cache provider for blob URL resolution
 */
export interface ImageCacheProvider {
  getCachedBlobUrl: (url: string) => Promise<string | null>;
  preloadedUrls?: Set<string>;
}

/**
 * Decodes a single image URL, using cached blob URL if available.
 * Blob URLs resolve nearly instantly since data is local.
 *
 * @param url - Image URL to decode
 * @param cacheProvider - Optional cache provider for blob URL lookup
 * @returns Promise that resolves when image is decoded
 */
export const decodeImage = async (
  url: string,
  cacheProvider?: ImageCacheProvider,
): Promise<void> => {
  // If cache provider available, try to get blob URL
  let urlToLoad = url;

  if (cacheProvider) {
    // Quick check if URL is preloaded
    const isPreloaded = cacheProvider.preloadedUrls?.has(url);
    if (isPreloaded) {
      const blobUrl = await cacheProvider.getCachedBlobUrl(url);
      if (blobUrl) {
        urlToLoad = blobUrl;
        // Blob URLs are local data - decode is nearly instant
      }
    }
  }

  return new Promise<void>((resolve) => {
    const img = new Image();
    img.src = urlToLoad;
    if (typeof img.decode === 'function') {
      img
        .decode()
        .then(() => resolve())
        .catch(() => resolve());
    } else {
      img.onload = () => resolve();
      img.onerror = () => resolve();
    }
  });
};

/**
 * Decodes a list of image URLs in parallel with a timeout.
 *
 * @param urls - Array of image URLs to decode
 * @param timeoutMs - Maximum time to wait for all images (default: 2000ms)
 * @param cacheProvider - Optional cache provider for blob URL lookup
 * @returns Promise that resolves when all images are decoded or timeout is reached
 *
 * @example
 * await decodeImages(['/image1.jpg', '/image2.jpg']);
 *
 * // With cache provider for faster decoding
 * await decodeImages(urls, 2000, {
 *   getCachedBlobUrl: preloadOrchestrator.getCachedBlobUrl,
 *   preloadedUrls: preloadOrchestrator.preloadedUrls,
 * });
 */
export const decodeImages = async (
  urls: string[],
  timeoutMs = 2000,
  cacheProvider?: ImageCacheProvider,
): Promise<void> => {
  if (urls.length === 0) return;

  const decodePromises = urls.map((url) => decodeImage(url, cacheProvider));

  await Promise.race([
    Promise.all(decodePromises),
    new Promise<void>((resolve) => setTimeout(resolve, timeoutMs)),
  ]);
};

/**
 * Extracts all image URLs from a page (background + element images).
 *
 * @param page - Page object with background_image_url and ui_schema_json
 * @returns Array of resolved image URLs
 *
 * @example
 * const imageUrls = extractPageImageUrls(page);
 */
export const extractPageImageUrls = (page: PageWithImages | null): string[] => {
  if (!page) return [];

  const imageUrls: string[] = [];

  // Background image
  if (page.background_image_url) {
    const url = resolveAssetPlaybackUrl(page.background_image_url);
    if (url) imageUrls.push(url);
  }

  // Parse ui_schema_json for element images
  try {
    const uiSchema =
      typeof page.ui_schema_json === 'string'
        ? JSON.parse(page.ui_schema_json)
        : page.ui_schema_json;

    const pageElements = Array.isArray(uiSchema?.elements)
      ? uiSchema.elements
      : [];

    const { images: imageFields, nested: nestedFields } =
      PRELOAD_CONFIG.assetFields;

    pageElements.forEach((el: Record<string, unknown>) => {
      // Direct image fields
      imageFields.forEach((field) => {
        const value = el[field];
        if (typeof value === 'string' && value) {
          const url = resolveAssetPlaybackUrl(value);
          if (url && !imageUrls.includes(url)) imageUrls.push(url);
        }
      });

      // Nested arrays (galleryCards, carouselSlides)
      nestedFields.forEach((nestedField) => {
        const items = el[nestedField];
        if (Array.isArray(items)) {
          items.forEach((item: Record<string, unknown>) => {
            if (typeof item.imageUrl === 'string' && item.imageUrl) {
              const url = resolveAssetPlaybackUrl(item.imageUrl);
              if (url && !imageUrls.includes(url)) imageUrls.push(url);
            }
          });
        }
      });
    });
  } catch {
    // Ignore parse errors
  }

  return imageUrls;
};

/**
 * Waits for all images on a page to be decoded before continuing.
 * This eliminates white flash by ensuring images are ready to paint.
 *
 * @param page - Page object with images to decode
 * @param timeoutMs - Maximum time to wait (default: 2000ms)
 * @param cacheProvider - Optional cache provider for blob URL lookup (faster decoding)
 *
 * @example
 * await waitForPageImages(targetPage);
 * setActivePage(targetPage);
 *
 * // With cache provider for faster decoding
 * await waitForPageImages(targetPage, 2000, {
 *   getCachedBlobUrl: preloadOrchestrator.getCachedBlobUrl,
 *   preloadedUrls: preloadOrchestrator.preloadedUrls,
 * });
 */
export const waitForPageImages = async (
  page: PageWithImages | null,
  timeoutMs = 2000,
  cacheProvider?: ImageCacheProvider,
): Promise<void> => {
  const imageUrls = extractPageImageUrls(page);
  await decodeImages(imageUrls, timeoutMs, cacheProvider);
};
