const ImageProcessor = {
  async process(file, settings) {
    const warnings = [];
    let bitmap = null;
    
    // Decode image
    try {
      bitmap = await createImageBitmap(file);
    } catch (err) {
      const name = String(file?.name || '').toLowerCase();
      if (['.heic', '.heif'].some((ext) => name.endsWith(ext)) || ['image/heic', 'image/heif'].includes(file?.type)) {
        throw new Error('This browser cannot decode HEIC/HEIF here. Convert it on the device first or try Safari if the file came from iPhone.');
      }
      throw new Error('Failed to decode image: ' + err.message);
    }

    try {
      const originalWidth = bitmap.width;
      const originalHeight = bitmap.height;
      let sourceX = 0;
      let sourceY = 0;
      let sourceWidth = originalWidth;
      let sourceHeight = originalHeight;

      if (settings.cropAspect && settings.cropAspect !== 'free') {
        const [aspectW, aspectH] = String(settings.cropAspect).split(':').map(Number);
        if (aspectW > 0 && aspectH > 0) {
          const targetRatio = aspectW / aspectH;
          const sourceRatio = originalWidth / originalHeight;

          if (sourceRatio > targetRatio) {
            sourceWidth = Math.round(originalHeight * targetRatio);
          } else {
            sourceHeight = Math.round(originalWidth / targetRatio);
          }

          const zoom = Math.max(1, Math.min(Number(settings.cropZoom || 1), 2.5));
          sourceWidth = Math.max(1, Math.round(sourceWidth / zoom));
          sourceHeight = Math.max(1, Math.round(sourceHeight / zoom));
          sourceX = Math.round((originalWidth - sourceWidth) * ((settings.cropX ?? 50) / 100));
          sourceY = Math.round((originalHeight - sourceHeight) * ((settings.cropY ?? 50) / 100));

          warnings.push(`Cropped to ${settings.cropAspect}.`);
        }
      }

      // Calculate output dimensions
      let outWidth = sourceWidth;
      let outHeight = sourceHeight;
    
      const resizeMode = settings.resizeMode || (settings.maxWidth ? 'maxWidth' : 'original');

      if (resizeMode === 'maxWidth' && settings.maxWidth && sourceWidth > settings.maxWidth) {
        const ratio = settings.maxWidth / sourceWidth;
        outWidth = settings.maxWidth;
        outHeight = Math.round(sourceHeight * ratio);
        warnings.push(`Resized from ${sourceWidth}px to ${outWidth}px wide`);
      }

      if (resizeMode === 'maxHeight' && settings.maxHeight && sourceHeight > settings.maxHeight) {
        const ratio = settings.maxHeight / sourceHeight;
        outHeight = settings.maxHeight;
        outWidth = Math.round(sourceWidth * ratio);
        warnings.push(`Resized from ${sourceHeight}px to ${outHeight}px high`);
      }

      if (resizeMode === 'exact' && settings.exactWidth && settings.exactHeight) {
        outWidth = Math.max(1, settings.exactWidth);
        outHeight = Math.max(1, settings.exactHeight);
        warnings.push(`Resized to ${outWidth}x${outHeight}px`);
      }

      if (resizeMode === 'percent' && settings.resizePercent && settings.resizePercent > 0 && settings.resizePercent < 100) {
        const ratio = settings.resizePercent / 100;
        outWidth = Math.max(1, Math.round(sourceWidth * ratio));
        outHeight = Math.max(1, Math.round(sourceHeight * ratio));
        warnings.push(`Scaled to ${settings.resizePercent}%`);
      }

      // Check for upscaling prevention
      if (resizeMode === 'maxWidth' && settings.maxWidth && settings.maxWidth > originalWidth) {
        warnings.push('Original is smaller than target size. Keeping original dimensions.');
        outWidth = originalWidth;
        outHeight = originalHeight;
      }
      if (resizeMode === 'maxHeight' && settings.maxHeight && settings.maxHeight > originalHeight) {
        warnings.push('Original is smaller than target height. Keeping original dimensions.');
        outWidth = originalWidth;
        outHeight = originalHeight;
      }

      // Check transparency for JPEG
      if (settings.format === 'image/jpeg') {
        // Simple check: if it's a PNG, warn about transparency
        if (file.type === 'image/png') {
          warnings.push('JPEG does not support transparency. Transparent areas will become white.');
        }
      }

      // Check PNG warning
      if (settings.format === 'image/png' && file.type !== 'image/png') {
        warnings.push('PNG may be larger than the original. Use WebP for most website images.');
      }

      if (settings.format === 'image/avif') {
        warnings.push('AVIF is experimental and may not be supported in every browser.');
      }

      if (settings.removeMetadata === false) {
        warnings.push('Browser canvas export cannot preserve embedded metadata; optimized output is still re-encoded without it.');
      }

      // Render to canvas
      const canvas = document.createElement('canvas');
      canvas.width = outWidth;
      canvas.height = outHeight;
      const ctx = canvas.getContext('2d');
      if (!ctx) throw new Error('Canvas rendering is not available in this browser.');
    
      // Fill with white for JPEG (handles transparency)
      if (settings.format === 'image/jpeg') {
        ctx.fillStyle = '#FFFFFF';
        ctx.fillRect(0, 0, outWidth, outHeight);
      }
    
      ctx.drawImage(bitmap, sourceX, sourceY, sourceWidth, sourceHeight, 0, 0, outWidth, outHeight);

      // Export
      const encode = (qualityValue) => new Promise((resolve, reject) => {
        canvas.toBlob(
          (b) => {
            if (b) resolve(b);
            else reject(new Error(`Failed to create ${settings.format} output`));
          },
          settings.format,
          qualityValue
        );
      });

      let usedQuality = settings.format === 'image/png' ? null : settings.quality;
      let blob;
      if ((settings.autoQuality || settings.maxBytes) && settings.format !== 'image/png') {
        const preferredQuality = Math.max(1, Math.min(100, Number(settings.quality || 82)));
        const candidates = [...new Set([preferredQuality, 96, 92, 88, 84, 80, 76, 72, 68, 64, 60, 54, 48, 42, 36, 30])]
          .sort((a, b) => b - a);
        const encoded = [];
        for (const candidate of candidates) {
          encoded.push({ quality: candidate, blob: await encode(candidate / 100) });
        }
        const smallest = [...encoded].sort((a, b) => a.blob.size - b.blob.size)[0];
        const target = Number(settings.maxBytes || 0);
        const selected = target > 0
          ? (encoded.filter((item) => item.blob.size <= target).sort((a, b) => b.quality - a.quality)[0] || smallest)
          : (encoded.filter((item) => item.blob.size < file.size).sort((a, b) => b.quality - a.quality)[0] || smallest);
        blob = selected.blob;
        usedQuality = selected.quality;
        if (target > 0 && blob.size > target) {
          warnings.push(`Could not reach ${Math.round(target / 1024)} KB without going below practical quality. Selected ${usedQuality}%.`);
        } else if (target > 0) {
          warnings.push(`Target size reached at ${usedQuality}% quality.`);
        } else {
          warnings.push(`Auto quality selected ${usedQuality}%.`);
        }
      } else {
        if (settings.maxBytes && settings.format === 'image/png') {
          warnings.push('PNG export has no quality control here. Use WebP, JPEG or AVIF for target file size.');
        }
        const quality = settings.format === 'image/png' ? undefined : settings.quality / 100;
        blob = await encode(quality);
      }

      if (settings.format === 'image/avif' && blob.type !== 'image/avif') {
        warnings.push('This browser could not encode AVIF. Output may fall back to PNG.');
      }

      // Check if output is larger than original
      if (blob.size > file.size && settings.format !== 'image/png') {
        warnings.push('Optimized file is larger than original. Consider adjusting settings or keeping the original.');
      }

      return {
        blob,
        quality: usedQuality,
        dimensions: { w: outWidth, h: outHeight },
        originalDimensions: { w: originalWidth, h: originalHeight },
        warnings: warnings.length > 0 ? warnings : null
      };
    } finally {
      if (bitmap && typeof bitmap.close === 'function') bitmap.close();
    }
  }
};
