/**
 * PWA Preload Hook
 *
 * Orchestrates asset preloading for PWA offline caching.
 * Tracks progress and manages the preload overlay visibility.
 */

import { useCallback, useEffect, useState } from 'react';

type AssetToPreload = {
  url: string;
  type: 'image' | 'video' | 'audio' | 'other';
};

type PreloadState = {
  isPreloading: boolean;
  progress: number;
  loadedCount: number;
  totalCount: number;
  errors: string[];
};

type UsePWAPreloadOptions = {
  assets: AssetToPreload[];
  onComplete?: () => void;
  onError?: (errors: string[]) => void;
  skipIfCached?: boolean;
};

const CACHE_KEY_PREFIX = 'pwa_preload_';

const preloadImage = (url: string): Promise<void> => {
  return new Promise((resolve, reject) => {
    const img = new Image();
    img.onload = () => resolve();
    img.onerror = () => reject(new Error(`Failed to preload image: ${url}`));
    img.src = url;
  });
};

const preloadVideo = (url: string): Promise<void> => {
  return new Promise((resolve, reject) => {
    const video = document.createElement('video');
    video.preload = 'auto';
    video.onloadeddata = () => resolve();
    video.onerror = () => reject(new Error(`Failed to preload video: ${url}`));
    video.src = url;
    video.load();
  });
};

const preloadAudio = (url: string): Promise<void> => {
  return new Promise((resolve, reject) => {
    const audio = new Audio();
    audio.preload = 'auto';
    audio.onloadeddata = () => resolve();
    audio.onerror = () => reject(new Error(`Failed to preload audio: ${url}`));
    audio.src = url;
    audio.load();
  });
};

const preloadOther = (url: string): Promise<void> => {
  return fetch(url, { mode: 'no-cors' })
    .then(() => undefined)
    .catch(() => {
      throw new Error(`Failed to preload asset: ${url}`);
    });
};

const preloadAsset = async (asset: AssetToPreload): Promise<void> => {
  switch (asset.type) {
    case 'image':
      return preloadImage(asset.url);
    case 'video':
      return preloadVideo(asset.url);
    case 'audio':
      return preloadAudio(asset.url);
    default:
      return preloadOther(asset.url);
  }
};

export const usePWAPreload = (options: UsePWAPreloadOptions) => {
  const { assets, onComplete, onError, skipIfCached = true } = options;

  const [state, setState] = useState<PreloadState>({
    isPreloading: false,
    progress: 0,
    loadedCount: 0,
    totalCount: 0,
    errors: [],
  });

  const getCacheKey = useCallback(() => {
    if (!assets.length) return '';
    const assetUrls = assets
      .map((a) => a.url)
      .sort()
      .join('|');
    return `${CACHE_KEY_PREFIX}${btoa(assetUrls).slice(0, 32)}`;
  }, [assets]);

  const isCached = useCallback(() => {
    if (typeof window === 'undefined') return false;
    const cacheKey = getCacheKey();
    if (!cacheKey) return false;
    return sessionStorage.getItem(cacheKey) === 'true';
  }, [getCacheKey]);

  const markAsCached = useCallback(() => {
    if (typeof window === 'undefined') return;
    const cacheKey = getCacheKey();
    if (cacheKey) {
      sessionStorage.setItem(cacheKey, 'true');
    }
  }, [getCacheKey]);

  const startPreload = useCallback(async () => {
    if (!assets.length) {
      onComplete?.();
      return;
    }

    if (skipIfCached && isCached()) {
      setState((prev) => ({ ...prev, progress: 100, isPreloading: false }));
      onComplete?.();
      return;
    }

    setState({
      isPreloading: true,
      progress: 0,
      loadedCount: 0,
      totalCount: assets.length,
      errors: [],
    });

    const errors: string[] = [];
    let loadedCount = 0;

    // Preload assets with concurrency limit
    const concurrencyLimit = 3;
    const chunks: AssetToPreload[][] = [];
    for (let i = 0; i < assets.length; i += concurrencyLimit) {
      chunks.push(assets.slice(i, i + concurrencyLimit));
    }

    for (const chunk of chunks) {
      const results = await Promise.allSettled(
        chunk.map((asset) => preloadAsset(asset)),
      );

      results.forEach((result) => {
        loadedCount++;
        if (result.status === 'rejected') {
          errors.push(result.reason?.message || 'Unknown preload error');
        }
        setState((prev) => ({
          ...prev,
          loadedCount,
          progress: Math.round((loadedCount / assets.length) * 100),
          errors: [...errors],
        }));
      });
    }

    setState((prev) => ({
      ...prev,
      isPreloading: false,
      progress: 100,
    }));

    if (errors.length === 0) {
      markAsCached();
    }

    if (errors.length > 0) {
      onError?.(errors);
    }

    onComplete?.();
  }, [assets, isCached, markAsCached, onComplete, onError, skipIfCached]);

  // Auto-start preload when assets change
  useEffect(() => {
    if (assets.length > 0 && !state.isPreloading && state.progress === 0) {
      startPreload();
    }
  }, [assets, startPreload, state.isPreloading, state.progress]);

  return {
    ...state,
    startPreload,
    isCached: isCached(),
  };
};

export default usePWAPreload;
