/**
 * PWA Manifest Service
 *
 * Generates offline manifests for PWA asset downloads.
 *
 * SIMPLIFIED: Elements are now stored in ui_schema_json within tour_pages,
 * and transitions are stored as transitionVideoUrl in element content.
 * No need to query separate page_elements or transitions tables.
 */

const AssetsDBApi = require('../db/api/assets');
const AssetVariantsDBApi = require('../db/api/asset_variants');
const TourPagesDBApi = require('../db/api/tour_pages');

/**
 * Get asset type from MIME type or filename
 */
function getAssetType(mimeType, filename) {
  if (!mimeType && !filename) return 'other';

  const mime = (mimeType || '').toLowerCase();
  const name = (filename || '').toLowerCase();

  if (
    mime.startsWith('image/') ||
    /\.(jpg|jpeg|png|gif|webp|svg)$/.test(name)
  ) {
    return 'image';
  }
  if (mime.startsWith('video/') || /\.(mp4|webm|mov)$/.test(name)) {
    return 'video';
  }
  if (mime.startsWith('audio/') || /\.(mp3|wav|ogg|m4a)$/.test(name)) {
    return 'audio';
  }

  return 'other';
}

/**
 * Extract URLs from element content JSON
 */
function extractUrlsFromContent(contentJson) {
  if (!contentJson) return [];

  try {
    const content =
      typeof contentJson === 'string' ? JSON.parse(contentJson) : contentJson;
    const urls = [];

    const urlFields = [
      'iconUrl',
      'imageUrl',
      'mediaUrl',
      'videoUrl',
      'audioUrl',
      'transitionVideoUrl',
      'backgroundImageUrl',
      'reverseVideoUrl',
      'carouselPrevIconUrl',
      'carouselNextIconUrl',
      'src',
      'url',
      'poster',
      'thumbnail',
    ];

    const checkObject = (obj, 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)) {
          if (value.startsWith('http') || value.startsWith('/')) {
            urls.push({
              url: value,
              fieldType: key,
            });
          }
        } else if (typeof value === 'object' && value !== null) {
          checkObject(value, depth + 1);
        }
      }
    };

    checkObject(content);
    return urls;
  } catch {
    return [];
  }
}

class PWAManifestService {
  /**
   * Generate offline manifest for a project
   * @param {string} projectId - Project ID
   * @param {string} deviceType - 'mobile' or 'desktop' (affects variant selection)
   * @returns {Object} Offline manifest
   */
  static async generateManifest(projectId, deviceType = 'desktop') {
    // Fetch all project data
    const [assetsResult, pagesResult] = await Promise.all([
      AssetsDBApi.findAll({ project: projectId }, {}),
      TourPagesDBApi.findAll({ project: projectId }, {}),
    ]);

    const assets = assetsResult?.rows || [];
    const pages = pagesResult?.rows || [];

    // Build asset manifest entries
    const manifestAssets = [];
    const seenUrls = new Set();

    // Helper to convert size_mb to bytes
    const mbToBytes = (sizeMb) => {
      if (!sizeMb || isNaN(sizeMb)) return 0;
      return Math.round(parseFloat(sizeMb) * 1024 * 1024);
    };

    // Helper to add an asset to the manifest
    const addAsset = (
      id,
      url,
      filename,
      variantType,
      assetType,
      mimeType,
      sizeBytes,
      pageIds,
    ) => {
      if (!url || seenUrls.has(url)) return;
      seenUrls.add(url);

      manifestAssets.push({
        id: id || `url-${Date.now()}-${Math.random().toString(36).slice(2)}`,
        url,
        filename: filename || url.split('/').pop() || 'unknown',
        variantType: variantType || 'original',
        assetType: assetType || getAssetType(mimeType, filename),
        mimeType: mimeType || 'application/octet-stream',
        sizeBytes: sizeBytes || 0,
        pageIds: pageIds || [],
      });
    };

    // Add assets with their variants
    for (const asset of assets) {
      // Get asset variants
      const variants = await AssetVariantsDBApi.findAll(
        { asset: asset.id },
        {},
      );
      const variantRows = variants?.rows || [];

      // Select appropriate variants based on device type
      const selectedVariants = this.selectVariants(variantRows, deviceType);

      for (const variant of selectedVariants) {
        addAsset(
          variant.id,
          variant.url,
          variant.filename || asset.filename,
          variant.variant_type,
          asset.type || getAssetType(asset.mime_type, asset.filename),
          variant.mime_type || asset.mime_type,
          variant.size_bytes || mbToBytes(variant.size_mb),
          asset.pages?.map((p) => p.id) || [],
        );
      }

      // If no variants, add original (use cdn_url as primary, fall back to storage_key)
      if (
        selectedVariants.length === 0 &&
        (asset.cdn_url || asset.storage_key)
      ) {
        addAsset(
          asset.id,
          asset.cdn_url || asset.storage_key,
          asset.name || asset.filename,
          'original',
          asset.type || getAssetType(asset.mime_type, asset.name),
          asset.mime_type,
          mbToBytes(asset.size_mb),
          asset.pages?.map((p) => p.id) || [],
        );
      }
    }

    // Add page background images/videos and extract element URLs from ui_schema_json
    for (const page of pages) {
      if (page.background_image_url) {
        addAsset(
          `page-bg-${page.id}`,
          page.background_image_url,
          `page-${page.slug}-bg.jpg`,
          'original',
          'image',
          'image/jpeg',
          0,
          [page.id],
        );
      }
      if (page.background_video_url) {
        addAsset(
          `page-video-${page.id}`,
          page.background_video_url,
          `page-${page.slug}-video.mp4`,
          'original',
          'video',
          'video/mp4',
          0,
          [page.id],
        );
      }
      if (page.background_audio_url) {
        addAsset(
          `page-audio-${page.id}`,
          page.background_audio_url,
          `page-${page.slug}-audio.mp3`,
          'original',
          'audio',
          'audio/mpeg',
          0,
          [page.id],
        );
      }

      // Extract URLs from ui_schema_json elements
      try {
        const uiSchema =
          typeof page.ui_schema_json === 'string'
            ? JSON.parse(page.ui_schema_json)
            : page.ui_schema_json;

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

        for (const element of elements) {
          const contentUrls = extractUrlsFromContent(element);
          for (const { url, fieldType } of contentUrls) {
            const assetType =
              fieldType.includes('video') || fieldType.includes('transition')
                ? 'video'
                : fieldType.includes('audio')
                  ? 'audio'
                  : 'image';
            addAsset(
              `element-${page.id}-${element.id || fieldType}`,
              url,
              url.split('/').pop() || 'unknown',
              'original',
              assetType,
              null,
              0,
              [page.id],
            );
          }
        }
      } catch {
        // Skip pages with invalid ui_schema_json
      }
    }

    // Calculate total size
    const totalSizeBytes = manifestAssets.reduce(
      (sum, a) => sum + (a.sizeBytes || 0),
      0,
    );

    return {
      version: `v${Date.now()}`,
      projectId,
      projectSlug: '', // Would need project data
      assets: manifestAssets,
      totalSizeBytes,
      generatedAt: Date.now(),
    };
  }

  /**
   * Select appropriate variants based on device type
   */
  static selectVariants(variants, deviceType) {
    if (!variants || variants.length === 0) return [];

    const selected = [];

    // Prioritize variants based on device type
    const priority =
      deviceType === 'mobile'
        ? ['mp4_low', 'webp', 'thumbnail', 'preview', 'mp4_high', 'original']
        : ['mp4_high', 'webp', 'preview', 'mp4_low', 'thumbnail', 'original'];

    // Group variants by base asset
    const variantMap = new Map();
    for (const variant of variants) {
      const type = variant.variant_type;
      if (priority.includes(type)) {
        variantMap.set(type, variant);
      }
    }

    // Select best variant for each type
    for (const type of priority) {
      if (variantMap.has(type)) {
        selected.push(variantMap.get(type));
        break; // Take first matching priority
      }
    }

    return selected;
  }
}

module.exports = PWAManifestService;
