from os.path import splitext

from PIL import Image, ImageOps


def parse_aspect_ratio(aspect_ratio_str: str | None) -> tuple[float, float] | None:
    """Parse aspect ratio string into a tuple of floats.
    
    Args:
        aspect_ratio_str: Aspect ratio string in format 'W:H' (e.g., '16:9')
        
    Returns:
        Tuple of (width, height) floats, or None if not provided
        
    Raises:
        click.BadParameter: If format is invalid
    """
    if not aspect_ratio_str:
        return None
    
    try:
        parts = aspect_ratio_str.split(":")
        if len(parts) != 2:
            raise ValueError("Invalid aspect ratio format")
        return float(parts[0]), float(parts[1])
    except ValueError as e:
        raise ValueError(f"Invalid aspect ratio: {e}") from e


def process_image(
    input_path: str,
    output_path: str,
    output_format: str | None = None,
    max_size: int = 936,
    border_size: int = 72,
    border_color: tuple[int, int, int] = (250, 250, 250),
    aspect_ratio: tuple[float, float] | None = None
) -> None:
    with Image.open(input_path) as im:
        # Handle aspect ratio if specified
        if aspect_ratio:
            # Calculate target dimensions based on aspect ratio
            target_width, target_height = aspect_ratio
            ratio = target_width / target_height
            
            # Calculate the final image dimensions (including border)
            # We want the largest dimension to be exactly max_size
            if ratio >= 1:  # Landscape or square (width >= height)
                # Width will be max_size
                final_width_with_border = max_size
                final_height_with_border = int(max_size / ratio)
            else:  # Portrait (height > width)
                # Height will be max_size
                final_height_with_border = max_size
                final_width_with_border = int(max_size * ratio)
            
            # Now calculate content dimensions (excluding border)
            final_width = final_width_with_border - 2 * border_size
            final_height = final_height_with_border - 2 * border_size
        else:
            # No aspect ratio specified - maintain original image aspect ratio
            original_ratio = im.size[0] / im.size[1]
            
            # Calculate content dimensions that maintain original ratio
            content_size = max_size - 2 * border_size
            if content_size <= 0:
                raise ValueError(
                    f"Border size {border_size} is too large for max size {max_size}"
                )
            
            # Fit within content_size while preserving original aspect ratio
            if original_ratio >= 1:  # Landscape or square
                final_width = content_size
                final_height = int(content_size / original_ratio)
            else:  # Portrait
                final_height = content_size
                final_width = int(content_size * original_ratio)
        
        # Resize image to fit the calculated dimensions
        # We need to resize to exactly the target dimensions, adding padding if necessary
        if im.size[0] != final_width or im.size[1] != final_height:
            # Create a new image with the target dimensions
            new_im = Image.new("RGB", (final_width, final_height), color=border_color)
            
            # Calculate how to fit the original image within the target dimensions
            # while preserving its aspect ratio
            original_ratio = im.size[0] / im.size[1]
            target_ratio = final_width / final_height
            
            if original_ratio > target_ratio:
                # Original is wider than target - fit to width
                new_width = final_width
                new_height = int(final_width / original_ratio)
            else:
                # Original is taller than target - fit to height
                new_height = final_height
                new_width = int(final_height * original_ratio)
            
            # Resize the original image
            resized_im = im.resize((new_width, new_height))
            
            # Calculate position to center the resized image
            paste_x = (final_width - new_width) // 2
            paste_y = (final_height - new_height) // 2
            
            new_im.paste(resized_im, (paste_x, paste_y))
            im = new_im
        
        # Determine output format
        if output_format is None:
            # Use input format if not specified
            output_format = splitext(input_path)[1][1:].upper()
            if output_format == "JPG":
                output_format = "JPEG"
        
        # Add border - final image will be exactly max_size on the largest dimension
        final_image = ImageOps.expand(
            image=im,
            border=border_size,
            fill=border_color
        )
        
        final_image.save(fp=output_path, format=output_format)
