/**
 * Asset Discovery Module
 *
 * Shared asset extraction logic for both online preload and offline download systems.
 * This module provides a single source of truth for discovering assets from pages,
 * elements, and navigation links.
 */

import { PRELOAD_CONFIG } from '../../config/preload.config';
import { extractStoragePath } from '../assetUrl';
import type {
  PreloadPage,
  PreloadPageLink,
  PreloadElement,
  PreloadAssetInfo,
} from '../../types/preload';
import type { AssetType } from '../../types/offline';

/**
 * Asset information for caching
 */
export interface AssetToCache {
  storageKey: string;
  originalUrl: string;
  assetType: AssetType;
  pageId: string;
  priority: number;
  sizeBytes?: number;
  isTransition?: boolean;
}

/**
 * Options for asset discovery
 */
export interface AssetDiscoveryOptions {
  includeTransitions?: boolean;
  includeBackgrounds?: boolean;
  includeElementAssets?: boolean;
}

const DEFAULT_OPTIONS: AssetDiscoveryOptions = {
  includeTransitions: true,
  includeBackgrounds: true,
  includeElementAssets: true,
};

/**
 * Classify asset type based on field name
 */
export function classifyAssetType(
  fieldName: string,
  url: string,
): AssetType {
  const lowerField = fieldName.toLowerCase();
  const lowerUrl = url.toLowerCase();

  // Check field name first (more reliable)
  if (lowerField.includes('transition')) {
    return 'transition';
  }
  if (lowerField.includes('video')) {
    return 'video';
  }
  if (lowerField.includes('audio')) {
    return 'audio';
  }
  if (
    lowerField.includes('image') ||
    lowerField.includes('icon') ||
    lowerField.includes('poster') ||
    lowerField.includes('thumbnail') ||
    lowerField.includes('src')
  ) {
    return 'image';
  }

  // Fallback: check URL extension
  if (lowerUrl.match(/\.(mp4|webm|mov|avi|mkv)/)) {
    return 'video';
  }
  if (lowerUrl.match(/\.(mp3|wav|ogg|aac|m4a)/)) {
    return 'audio';
  }
  if (lowerUrl.match(/\.(jpg|jpeg|png|gif|webp|avif|svg)/)) {
    return 'image';
  }

  return 'other';
}

/**
 * Extract assets from element content_json
 */
export function extractElementAssets(
  elements: PreloadElement[],
  pageId?: string,
): AssetToCache[] {
  const assets: AssetToCache[] = [];
  const seenUrls = new Set<string>();

  const urlFields = PRELOAD_CONFIG.assetFields.all as readonly string[];

  const processElement = (element: PreloadElement) => {
    const elementPageId = pageId || element.pageId || '';
    if (!element.content_json) return;

    try {
      const content =
        typeof element.content_json === 'string'
          ? JSON.parse(element.content_json)
          : element.content_json;

      const checkObject = (obj: Record<string, unknown>, depth = 0) => {
        if (depth > 5 || !obj || typeof obj !== 'object') return;

        for (const [key, value] of Object.entries(obj)) {
          if (typeof value === 'string' && value && urlFields.includes(key)) {
            const storageKey = extractStoragePath(value);
            if (storageKey && !seenUrls.has(storageKey)) {
              seenUrls.add(storageKey);
              const assetType = classifyAssetType(key, value);
              assets.push({
                storageKey,
                originalUrl: value,
                assetType,
                pageId: elementPageId,
                priority: 0, // Will be calculated later
              });
            }
          } else if (typeof value === 'object' && value !== null) {
            checkObject(value as Record<string, unknown>, depth + 1);
          }
        }
      };

      checkObject(content);
    } catch {
      // Ignore parse errors
    }
  };

  elements.forEach(processElement);
  return assets;
}

/**
 * Extract background assets from a page
 */
export function extractPageBackgroundAssets(page: PreloadPage): AssetToCache[] {
  const assets: AssetToCache[] = [];

  if (page.background_image_url) {
    const storageKey = extractStoragePath(page.background_image_url);
    if (storageKey) {
      assets.push({
        storageKey,
        originalUrl: page.background_image_url,
        assetType: 'image',
        pageId: page.id,
        priority: 0,
      });
    }
  }

  if (page.background_video_url) {
    const storageKey = extractStoragePath(page.background_video_url);
    if (storageKey) {
      assets.push({
        storageKey,
        originalUrl: page.background_video_url,
        assetType: 'video',
        pageId: page.id,
        priority: 0,
      });
    }
  }

  if (page.background_audio_url) {
    const storageKey = extractStoragePath(page.background_audio_url);
    if (storageKey) {
      assets.push({
        storageKey,
        originalUrl: page.background_audio_url,
        assetType: 'audio',
        pageId: page.id,
        priority: 0,
      });
    }
  }

  return assets;
}

/**
 * Extract transition video assets from page links
 */
export function extractTransitionAssets(
  pageLinks: PreloadPageLink[],
  fromPageId?: string,
): AssetToCache[] {
  const assets: AssetToCache[] = [];
  const seenUrls = new Set<string>();

  const activeLinks = pageLinks.filter((link) => {
    if (link.is_active === false) return false;
    if (fromPageId && link.from_pageId !== fromPageId) return false;
    return true;
  });

  activeLinks.forEach((link) => {
    const transition = link.transition as { video_url?: string } | undefined;
    if (transition?.video_url) {
      const storageKey = extractStoragePath(transition.video_url);
      if (storageKey && !seenUrls.has(storageKey)) {
        seenUrls.add(storageKey);
        assets.push({
          storageKey,
          originalUrl: transition.video_url,
          assetType: 'transition',
          pageId: link.from_pageId || '',
          priority: 0,
          isTransition: true,
        });
      }
    }
  });

  return assets;
}

/**
 * Calculate priority based on asset type and page relationship
 */
export function calculateAssetPriority(
  assetType: AssetType,
  isCurrentPage: boolean,
  distance = 0,
): number {
  const typePriority = PRELOAD_CONFIG.priority.assetType[assetType] || 0;

  if (isCurrentPage) {
    return PRELOAD_CONFIG.priority.currentPage + typePriority;
  }

  // For neighbor pages, priority decreases with distance
  const basePriority =
    distance > 0 ? PRELOAD_CONFIG.priority.neighborBase / distance : 0;

  return basePriority + typePriority;
}

/**
 * Discover all assets for a set of pages
 * This is the main entry point for asset discovery
 */
export function discoverPageAssets(
  pageIds: string[],
  pages: PreloadPage[],
  pageLinks: PreloadPageLink[],
  elements: PreloadElement[],
  options: AssetDiscoveryOptions = DEFAULT_OPTIONS,
): AssetToCache[] {
  const assets: AssetToCache[] = [];
  const seenStorageKeys = new Set<string>();

  const addAsset = (asset: AssetToCache) => {
    if (!seenStorageKeys.has(asset.storageKey)) {
      seenStorageKeys.add(asset.storageKey);
      assets.push(asset);
    }
  };

  pageIds.forEach((pageId) => {
    const page = pages.find((p) => p.id === pageId);

    // Extract background assets
    if (options.includeBackgrounds && page) {
      extractPageBackgroundAssets(page).forEach(addAsset);
    }

    // Extract element assets
    if (options.includeElementAssets) {
      const pageElements = elements.filter((el) => el.pageId === pageId);
      extractElementAssets(pageElements, pageId).forEach(addAsset);
    }

    // Extract transition assets
    if (options.includeTransitions) {
      extractTransitionAssets(pageLinks, pageId).forEach(addAsset);
    }
  });

  return assets;
}

/**
 * Discover all assets for a project (used by offline download)
 * Includes all pages, all elements, and all transitions
 */
export function discoverProjectAssets(
  pages: PreloadPage[],
  pageLinks: PreloadPageLink[],
  elements: PreloadElement[],
  options: AssetDiscoveryOptions = DEFAULT_OPTIONS,
): AssetToCache[] {
  const pageIds = pages.map((p) => p.id);
  return discoverPageAssets(pageIds, pages, pageLinks, elements, options);
}

/**
 * Get prioritized assets for preloading based on current page and neighbor depth
 */
export function getPrioritizedAssets(
  currentPageId: string,
  pages: PreloadPage[],
  pageLinks: PreloadPageLink[],
  elements: PreloadElement[],
  neighborPageIds: Array<{ pageId: string; distance: number }>,
  options: AssetDiscoveryOptions = DEFAULT_OPTIONS,
): AssetToCache[] {
  const assets: AssetToCache[] = [];
  const seenStorageKeys = new Set<string>();

  const addAssetWithPriority = (
    asset: AssetToCache,
    isCurrentPage: boolean,
    distance: number,
  ) => {
    if (seenStorageKeys.has(asset.storageKey)) return;
    seenStorageKeys.add(asset.storageKey);

    assets.push({
      ...asset,
      priority: calculateAssetPriority(asset.assetType, isCurrentPage, distance),
    });
  };

  // Current page assets (highest priority)
  const currentPage = pages.find((p) => p.id === currentPageId);
  if (currentPage) {
    if (options.includeBackgrounds) {
      extractPageBackgroundAssets(currentPage).forEach((asset) =>
        addAssetWithPriority(asset, true, 0),
      );
    }

    if (options.includeElementAssets) {
      const currentPageElements = elements.filter(
        (el) => el.pageId === currentPageId,
      );
      extractElementAssets(currentPageElements, currentPageId).forEach((asset) =>
        addAssetWithPriority(asset, true, 0),
      );
    }

    if (options.includeTransitions) {
      extractTransitionAssets(pageLinks, currentPageId).forEach((asset) =>
        addAssetWithPriority(asset, true, 0),
      );
    }
  }

  // Neighbor page assets (decreasing priority with distance)
  neighborPageIds.forEach(({ pageId, distance }) => {
    const page = pages.find((p) => p.id === pageId);
    if (page) {
      if (options.includeBackgrounds) {
        extractPageBackgroundAssets(page).forEach((asset) =>
          addAssetWithPriority(asset, false, distance),
        );
      }

      if (options.includeElementAssets) {
        const pageElements = elements.filter((el) => el.pageId === pageId);
        extractElementAssets(pageElements, pageId).forEach((asset) =>
          addAssetWithPriority(asset, false, distance),
        );
      }

      if (options.includeTransitions) {
        extractTransitionAssets(pageLinks, pageId).forEach((asset) =>
          addAssetWithPriority(asset, false, distance),
        );
      }
    }
  });

  // Sort by priority (highest first)
  return assets.sort((a, b) => b.priority - a.priority);
}

/**
 * Convert AssetToCache to PreloadAssetInfo for backward compatibility
 */
export function toPreloadAssetInfo(asset: AssetToCache): PreloadAssetInfo {
  // Map 'transition' to appropriate type for PreloadAssetInfo
  const assetType =
    asset.assetType === 'transition' || asset.assetType === 'other'
      ? 'other'
      : asset.assetType;

  return {
    url: asset.originalUrl,
    pageId: asset.pageId,
    assetType: assetType as 'image' | 'video' | 'audio' | 'transition' | 'other',
    priority: asset.priority,
  };
}
