import { supabase } from '@/lib/supabaseClient';
import { invokeWithTracking } from './edgeFunctionTracking';
import type { UploadedImage } from '@/components/upload/EntityMultiImageUploader';
import { handleError, handleNonCriticalError } from './errorHandler';

export interface CloudflareUploadResponse {
  result: {
    id: string;
    variants: string[];
  };
  success: boolean;
}

// Internal type to track upload status
interface UploadedImageWithFlag extends UploadedImage {
  wasNewlyUploaded?: boolean;
}

// Upload timeout in milliseconds (30 seconds)
const UPLOAD_TIMEOUT_MS = 30000;

/**
 * Creates a promise that rejects after a timeout
 */
function withTimeout<T>(promise: Promise<T>, timeoutMs: number, operation: string): Promise<T> {
  return Promise.race([
    promise,
    new Promise<T>((_, reject) =>
      setTimeout(() => reject(new Error(`${operation} timed out after ${timeoutMs}ms`)), timeoutMs)
    )
  ]);
}

/**
 * Uploads pending local images to Cloudflare via Supabase Edge Function
 * @param images Array of UploadedImage objects (mix of local and already uploaded)
 * @returns Array of UploadedImage objects with all images uploaded
 */
export async function uploadPendingImages(images: UploadedImage[]): Promise<UploadedImage[]> {
  // Process all images in parallel for better performance using allSettled
  const uploadPromises = images.map(async (image, index): Promise<UploadedImageWithFlag> => {
    if (image.isLocal && image.file) {
      const fileName = image.file.name;
      
      // Step 1: Get upload URL from our Supabase Edge Function (with tracking and timeout)
      const { data: uploadUrlData, error: urlError, requestId } = await withTimeout(
        invokeWithTracking(
          'upload-image',
          { action: 'get-upload-url' }
        ),
        UPLOAD_TIMEOUT_MS,
        'Get upload URL'
      );

      if (urlError || !uploadUrlData?.uploadURL) {
        const error = new Error(`Failed to get upload URL for "${fileName}": ${urlError?.message || 'Unknown error'}`);
        handleError(error, {
          action: 'Get Upload URL',
          metadata: { fileName, requestId }
        });
        throw error;
      }


      // Step 2: Upload file directly to Cloudflare with retry on transient failures
      const formData = new FormData();
      formData.append('file', image.file);

      const { withRetry } = await import('./retryHelpers');
      const uploadResponse = await withRetry(
        () => withTimeout(
          fetch(uploadUrlData.uploadURL, {
            method: 'POST',
            body: formData,
          }),
          UPLOAD_TIMEOUT_MS,
          'Cloudflare upload'
        ),
        {
          maxAttempts: 3,
          baseDelay: 500,
          shouldRetry: (error) => {
            // Retry on network errors, timeouts, or 5xx errors
            if (error instanceof Error) {
              const msg = error.message.toLowerCase();
              if (msg.includes('timeout')) return true;
              if (msg.includes('network')) return true;
              if (msg.includes('failed to fetch')) return true;
            }
            return false;
          }
        }
      );

      if (!uploadResponse.ok) {
        const errorText = await uploadResponse.text();
        const error = new Error(`Upload failed for "${fileName}" (status ${uploadResponse.status}): ${errorText}`);
        handleError(error, {
          action: 'Cloudflare Upload',
          metadata: { fileName, status: uploadResponse.status, timeout_ms: UPLOAD_TIMEOUT_MS }
        });
        throw error;
      }

      const result: CloudflareUploadResponse = await uploadResponse.json();

      if (!result.success || !result.result) {
        const error = new Error(`Cloudflare upload returned unsuccessful response for "${fileName}"`);
        handleError(error, {
          action: 'Cloudflare Upload',
          metadata: { fileName }
        });
        throw error;
      }


      // Clean up object URL
      URL.revokeObjectURL(image.url);
      
      // Step 3: Return uploaded image metadata with wasNewlyUploaded flag
      return {
        url: `https://cdn.thrillwiki.com/images/${result.result.id}/public`,
        cloudflare_id: result.result.id,
        caption: image.caption,
        isLocal: false,
        wasNewlyUploaded: true // Flag to track newly uploaded images
      };
    } else {
      // Already uploaded, keep as is
      return {
        url: image.url,
        cloudflare_id: image.cloudflare_id,
        caption: image.caption,
        isLocal: false,
        wasNewlyUploaded: false // Pre-existing image
      };
    }
  });

  // Wait for all uploads to settle (succeed or fail)
  const results = await Promise.allSettled(uploadPromises);
  
  // Separate successful and failed uploads
  const successfulUploads: UploadedImageWithFlag[] = [];
  const newlyUploadedImageIds: string[] = []; // Track ONLY newly uploaded images for cleanup
  const errors: string[] = [];

  results.forEach((result, index) => {
    if (result.status === 'fulfilled') {
      const uploadedImage = result.value;
      successfulUploads.push(uploadedImage);
      // Only track newly uploaded images for potential cleanup
      if (uploadedImage.wasNewlyUploaded && uploadedImage.cloudflare_id) {
        newlyUploadedImageIds.push(uploadedImage.cloudflare_id);
      }
    } else {
      errors.push(result.reason?.message || `Upload ${index + 1} failed`);
    }
  });

  // If any uploads failed, clean up ONLY newly uploaded images and throw error
  if (errors.length > 0) {
    if (newlyUploadedImageIds.length > 0) {
      const cleanupError = new Error(`Some uploads failed, cleaning up ${newlyUploadedImageIds.length} newly uploaded images`);
      handleError(cleanupError, {
        action: 'Upload Cleanup',
        metadata: {
          newlyUploadedCount: newlyUploadedImageIds.length,
          failureCount: errors.length
        }
      });
      
      // Attempt cleanup in parallel with detailed error tracking
      const cleanupResults = await Promise.allSettled(
        newlyUploadedImageIds.map(imageId => 
          invokeWithTracking('upload-image', {
            action: 'delete',
            imageId,
          })
        )
      );

      // Track cleanup failures silently (non-critical)
      const cleanupFailures = cleanupResults.filter(r => r.status === 'rejected');
      if (cleanupFailures.length > 0) {
        handleNonCriticalError(
          new Error(`Failed to cleanup ${cleanupFailures.length} of ${newlyUploadedImageIds.length} images`),
          {
            action: 'Image Cleanup',
            metadata: {
              cleanupFailures: cleanupFailures.length,
              totalCleanup: newlyUploadedImageIds.length,
              orphanedImages: newlyUploadedImageIds.filter((_, i) => cleanupResults[i].status === 'rejected')
            }
          }
        );
      }
    }

    const finalError = new Error(`Failed to upload ${errors.length} of ${images.length} images: ${errors.join('; ')}`);
    handleError(finalError, {
      action: 'Image Upload',
      metadata: { failureCount: errors.length, totalCount: images.length }
    });
    throw finalError;
  }

  // Remove the wasNewlyUploaded flag before returning
  return successfulUploads.map(({ wasNewlyUploaded, ...image }) => image as UploadedImage);
}
