"""
Product Image Optimizer Module
Compresses and resizes images while maintaining quality
✨ NEW: Target file size optimization (e.g., compress to 50KB, 100KB)
"""

from PIL import Image
import io
import base64
import os


class ProductImageOptimizer:
    """Optimize images by compressing and resizing"""
    
    # Default settings
    MAX_WIDTH = 1200  # Maximum width in pixels
    MAX_HEIGHT = 1200  # Maximum height in pixels
    JPEG_QUALITY = 85  # JPEG quality (1-100)
    PNG_OPTIMIZE = True  # Optimize PNG files
    WEBP_QUALITY = 90  # WebP quality (better compression than JPEG)
    
    # Target file size presets (in bytes)
    PRESET_THUMBNAIL = 30 * 1024  # 30KB
    PRESET_SMALL = 50 * 1024      # 50KB
    PRESET_MEDIUM = 100 * 1024    # 100KB
    PRESET_LARGE = 200 * 1024     # 200KB
    
    @staticmethod
    def optimize_to_target_size(base64_data, target_size_kb, max_width=None, max_height=None):
        """
        Optimize image to reach a specific target file size
        
        Args:
            base64_data: Base64 string with data URI prefix
            target_size_kb: Target file size in kilobytes (e.g., 50 for 50KB)
            max_width: Maximum width (default: 1200)
            max_height: Maximum height (default: 1200)
            
        Returns:
            Optimized base64 string or None if failed
        """
        if not base64_data or 'base64,' not in base64_data:
            return None
        
        target_size_bytes = target_size_kb * 1024
        max_width = max_width or ProductImageOptimizer.MAX_WIDTH
        max_height = max_height or ProductImageOptimizer.MAX_HEIGHT
        
        try:
            # Extract and decode
            header, encoded = base64_data.split(',', 1)
            image_data = base64.b64decode(encoded)
            image = Image.open(io.BytesIO(image_data))
            
            # Convert to RGB if needed
            if image.mode in ('RGBA', 'LA', 'P'):
                background = Image.new('RGB', image.size, (255, 255, 255))
                if image.mode == 'P':
                    image = image.convert('RGBA')
                background.paste(image, mask=image.split()[-1] if image.mode == 'RGBA' else None)
                image = background
            elif image.mode not in ('RGB', 'L'):
                image = image.convert('RGB')
            
            # Get original size
            original_width, original_height = image.size
            
            # Try different strategies to reach target size
            # Strategy 1: Reduce dimensions first
            current_width, current_height = original_width, original_height
            
            # If image is larger than max dimensions, resize first
            if current_width > max_width or current_height > max_height:
                width_ratio = max_width / current_width
                height_ratio = max_height / current_height
                scale_factor = min(width_ratio, height_ratio)
                current_width = int(current_width * scale_factor)
                current_height = int(current_height * scale_factor)
                image = image.resize((current_width, current_height), Image.Resampling.LANCZOS)
                print(f"📐 Initial resize: {original_width}x{original_height} → {current_width}x{current_height}")
            
            # Strategy 2: Binary search for optimal quality
            min_quality = 10
            max_quality = 95
            best_result = None
            best_size_diff = float('inf')
            
            for attempt in range(15):  # Maximum 15 attempts
                quality = (min_quality + max_quality) // 2
                
                # Try this quality
                output = io.BytesIO()
                image.save(output, format='JPEG', quality=quality, optimize=True)
                file_size = len(output.getvalue())
                size_diff = abs(file_size - target_size_bytes)
                
                print(f"  Attempt {attempt + 1}: quality={quality}, size={file_size:,} bytes (target={target_size_bytes:,})")
                
                # Check if this is the best so far
                if size_diff < best_size_diff:
                    best_size_diff = size_diff
                    best_result = output.getvalue()
                
                # Perfect match or close enough (within 5%)
                tolerance = target_size_bytes * 0.05
                if size_diff <= tolerance:
                    print(f"✅ Target achieved! Size: {file_size:,} bytes (target: {target_size_bytes:,})")
                    break
                
                # Adjust quality range
                if file_size > target_size_bytes:
                    max_quality = quality - 1
                else:
                    min_quality = quality + 1
                
                # If range exhausted, try reducing dimensions
                if min_quality > max_quality:
                    # Reduce dimensions by 10%
                    current_width = int(current_width * 0.9)
                    current_height = int(current_height * 0.9)
                    
                    if current_width < 100 or current_height < 100:
                        print(f"⚠️  Minimum dimensions reached")
                        break
                    
                    image = image.resize((current_width, current_height), Image.Resampling.LANCZOS)
                    print(f"  📐 Reducing dimensions: {current_width}x{current_height}")
                    
                    # Reset quality range
                    min_quality = 10
                    max_quality = 95
            
            if best_result is None:
                return None
            
            # Encode to base64
            optimized_base64 = base64.b64encode(best_result).decode('utf-8')
            optimized_data_uri = f"data:image/jpeg;base64,{optimized_base64}"
            
            original_size = len(image_data)
            final_size = len(best_result)
            compression_ratio = (1 - (final_size / original_size)) * 100
            
            print(f"🎯 Final: {original_size:,} → {final_size:,} bytes ({compression_ratio:.1f}% reduction)")
            print(f"   Target was: {target_size_bytes:,} bytes, achieved: {final_size:,} bytes")
            
            return optimized_data_uri
            
        except Exception as e:
            print(f"❌ Error in target size optimization: {e}")
            import traceback
            traceback.print_exc()
            return None
    
    @staticmethod
    def optimize_base64_image(base64_data, max_width=None, max_height=None, 
                              quality=None, output_format=None):
        """
        Optimize a base64 encoded image
        
        Args:
            base64_data: Base64 string with data URI prefix
            max_width: Maximum width (default: 1200)
            max_height: Maximum height (default: 1200)
            quality: Compression quality 1-100 (default: 85)
            output_format: Force output format ('JPEG', 'PNG', 'WEBP')
            
        Returns:
            Optimized base64 string with data URI prefix
        """
        if not base64_data or 'base64,' not in base64_data:
            return None
        
        # Set defaults
        max_width = max_width or ProductImageOptimizer.MAX_WIDTH
        max_height = max_height or ProductImageOptimizer.MAX_HEIGHT
        quality = quality or ProductImageOptimizer.JPEG_QUALITY
        
        try:
            # Extract format and data
            header, encoded = base64_data.split(',', 1)
            
            # Decode base64 to bytes
            image_data = base64.b64decode(encoded)
            
            # Open image with PIL
            image = Image.open(io.BytesIO(image_data))
            
            # Convert RGBA to RGB if needed (for JPEG compatibility)
            if image.mode in ('RGBA', 'LA', 'P'):
                # Create white background
                background = Image.new('RGB', image.size, (255, 255, 255))
                if image.mode == 'P':
                    image = image.convert('RGBA')
                background.paste(image, mask=image.split()[-1] if image.mode == 'RGBA' else None)
                image = background
            elif image.mode not in ('RGB', 'L'):
                image = image.convert('RGB')
            
            # Get original dimensions
            original_width, original_height = image.size
            
            # Calculate new dimensions (maintain aspect ratio)
            if original_width > max_width or original_height > max_height:
                # Calculate scaling factor
                width_ratio = max_width / original_width
                height_ratio = max_height / original_height
                scale_factor = min(width_ratio, height_ratio)
                
                new_width = int(original_width * scale_factor)
                new_height = int(original_height * scale_factor)
                
                # Resize image using high-quality Lanczos resampling
                image = image.resize((new_width, new_height), Image.Resampling.LANCZOS)
                
                print(f"📐 Resized: {original_width}x{original_height} → {new_width}x{new_height}")
            
            # Determine output format
            if output_format:
                save_format = output_format.upper()
            elif 'image/jpeg' in header or 'image/jpg' in header:
                save_format = 'JPEG'
            elif 'image/png' in header:
                save_format = 'PNG'
            elif 'image/webp' in header:
                save_format = 'WEBP'
            else:
                # Default to JPEG for best compression
                save_format = 'JPEG'
            
            # Save optimized image to bytes
            output = io.BytesIO()
            
            if save_format == 'JPEG':
                image.save(output, format='JPEG', quality=quality, optimize=True)
                mime_type = 'image/jpeg'
            elif save_format == 'PNG':
                image.save(output, format='PNG', optimize=ProductImageOptimizer.PNG_OPTIMIZE)
                mime_type = 'image/png'
            elif save_format == 'WEBP':
                image.save(output, format='WEBP', quality=ProductImageOptimizer.WEBP_QUALITY, method=6)
                mime_type = 'image/webp'
            else:
                image.save(output, format='JPEG', quality=quality, optimize=True)
                mime_type = 'image/jpeg'
            
            # Get optimized bytes
            optimized_bytes = output.getvalue()
            
            # Encode back to base64
            optimized_base64 = base64.b64encode(optimized_bytes).decode('utf-8')
            
            # Create data URI
            optimized_data_uri = f"data:{mime_type};base64,{optimized_base64}"
            
            # Calculate compression ratio
            original_size = len(image_data)
            optimized_size = len(optimized_bytes)
            compression_ratio = (1 - (optimized_size / original_size)) * 100
            
            print(f"🗜️  Compression: {original_size:,} bytes → {optimized_size:,} bytes ({compression_ratio:.1f}% reduction)")
            
            return optimized_data_uri
            
        except Exception as e:
            print(f"❌ Error optimizing image: {e}")
            import traceback
            traceback.print_exc()
            return None
    
    @staticmethod
    def optimize_file(file_path, output_path=None, max_width=None, max_height=None, 
                      quality=None, output_format=None):
        """
        Optimize an image file
        
        Args:
            file_path: Path to input image
            output_path: Path to save optimized image (default: overwrite input)
            max_width: Maximum width (default: 1200)
            max_height: Maximum height (default: 1200)
            quality: Compression quality 1-100 (default: 85)
            output_format: Force output format ('JPEG', 'PNG', 'WEBP')
            
        Returns:
            Path to optimized file
        """
        if not os.path.exists(file_path):
            return None
        
        # Set defaults
        max_width = max_width or ProductImageOptimizer.MAX_WIDTH
        max_height = max_height or ProductImageOptimizer.MAX_HEIGHT
        quality = quality or ProductImageOptimizer.JPEG_QUALITY
        output_path = output_path or file_path
        
        try:
            # Open image
            image = Image.open(file_path)
            
            # Convert RGBA to RGB if needed
            if image.mode in ('RGBA', 'LA', 'P'):
                background = Image.new('RGB', image.size, (255, 255, 255))
                if image.mode == 'P':
                    image = image.convert('RGBA')
                background.paste(image, mask=image.split()[-1] if image.mode == 'RGBA' else None)
                image = background
            elif image.mode not in ('RGB', 'L'):
                image = image.convert('RGB')
            
            # Get original dimensions
            original_width, original_height = image.size
            
            # Calculate new dimensions
            if original_width > max_width or original_height > max_height:
                width_ratio = max_width / original_width
                height_ratio = max_height / original_height
                scale_factor = min(width_ratio, height_ratio)
                
                new_width = int(original_width * scale_factor)
                new_height = int(original_height * scale_factor)
                
                image = image.resize((new_width, new_height), Image.Resampling.LANCZOS)
                
                print(f"📐 Resized: {original_width}x{original_height} → {new_width}x{new_height}")
            
            # Determine output format
            if output_format:
                save_format = output_format.upper()
            else:
                # Detect from file extension
                ext = os.path.splitext(file_path)[1].lower()
                if ext in ['.jpg', '.jpeg']:
                    save_format = 'JPEG'
                elif ext == '.png':
                    save_format = 'PNG'
                elif ext == '.webp':
                    save_format = 'WEBP'
                else:
                    save_format = 'JPEG'
            
            # Get original size
            original_size = os.path.getsize(file_path)
            
            # Save optimized image
            if save_format == 'JPEG':
                image.save(output_path, format='JPEG', quality=quality, optimize=True)
            elif save_format == 'PNG':
                image.save(output_path, format='PNG', optimize=ProductImageOptimizer.PNG_OPTIMIZE)
            elif save_format == 'WEBP':
                image.save(output_path, format='WEBP', quality=ProductImageOptimizer.WEBP_QUALITY, method=6)
            
            # Get optimized size
            optimized_size = os.path.getsize(output_path)
            compression_ratio = (1 - (optimized_size / original_size)) * 100
            
            print(f"🗜️  Compression: {original_size:,} bytes → {optimized_size:,} bytes ({compression_ratio:.1f}% reduction)")
            
            return output_path
            
        except Exception as e:
            print(f"❌ Error optimizing file: {e}")
            import traceback
            traceback.print_exc()
            return None


# Quick helper functions
def optimize_image_base64(base64_data, quality=85, max_size=1200):
    """Quick helper to optimize base64 image"""
    return ProductImageOptimizer.optimize_base64_image(
        base64_data, 
        max_width=max_size, 
        max_height=max_size, 
        quality=quality
    )


def optimize_image_file(file_path, quality=85, max_size=1200):
    """Quick helper to optimize image file"""
    return ProductImageOptimizer.optimize_file(
        file_path, 
        max_width=max_size, 
        max_height=max_size, 
        quality=quality
    )


# ✨ NEW: Target file size helpers
def optimize_to_50kb(base64_data):
    """Optimize image to approximately 50KB"""
    return ProductImageOptimizer.optimize_to_target_size(base64_data, target_size_kb=50)


def optimize_to_100kb(base64_data):
    """Optimize image to approximately 100KB"""
    return ProductImageOptimizer.optimize_to_target_size(base64_data, target_size_kb=100)


def optimize_to_size(base64_data, target_kb):
    """Optimize image to specific target size in KB"""
    return ProductImageOptimizer.optimize_to_target_size(base64_data, target_size_kb=target_kb)