#!/usr/bin/env python3
"""
图片处理工具模块

提供独立的图片处理功能，无需实例化Draft类即可调用
"""

from typing import Dict, Any, List, Tuple, Optional
from PIL import Image
import os


def load_image(image_source: Any) -> Image.Image:
    """
    加载图片，支持多种图片来源

    :param image_source: 图片来源，支持以下格式：
                        1. 字符串：本地图片文件路径或网络图片URL
                        2. bytes：图片二进制内容
                        3. PIL.Image对象：已打开的图片对象
    :return: PIL.Image对象
    :raises FileNotFoundError: 当本地图片文件不存在时
    :raises RuntimeError: 当加载网络图片失败时
    :raises TypeError: 当图片来源类型不支持时
    """
    if isinstance(image_source, str):
        # 检查是否为网络图片URL
        if image_source.startswith(("http://", "https://")):
            # 网络图片URL
            try:
                import requests
                from io import BytesIO

                response = requests.get(image_source, timeout=10)
                response.raise_for_status()
                image_content = response.content
                return Image.open(BytesIO(image_content))
            except Exception as e:
                raise RuntimeError(f"Failed to load image from URL: {str(e)}")
        else:
            # 本地图片文件路径
            if not os.path.exists(image_source):
                raise FileNotFoundError(f"Image file not found: {image_source}")
            return Image.open(image_source)
    elif isinstance(image_source, bytes):
        # 图片二进制内容
        from io import BytesIO

        return Image.open(BytesIO(image_source))
    elif isinstance(image_source, Image.Image):
        # 已打开的PIL Image对象
        return image_source
    else:
        raise TypeError(
            f"Unsupported image_source type: {type(image_source)}. "
            f"Supported types: str (file path or URL), bytes (image content), PIL.Image object"
        )


def get_crop_parameters(
    image_source: Any,
    start_point: Tuple[int, int],
    crop_width_px: int,
    auto_adjust: bool = True,
    supported_ratios: List[str] = None,
    return_format: str = "all",
) -> Optional[Dict[str, Any]]:
    """
    生成微信公众号草稿接口所需的图片裁剪参数
    支持多种图片输入方式和裁剪比例，提供灵活的裁剪参数生成

    :param image_source: 图片来源，支持以下格式：
                        1. 字符串：本地图片文件路径或网络图片URL
                        2. bytes：图片二进制内容
                        3. PIL.Image对象：已打开的图片对象
    :param start_point: 裁剪起点坐标 (x, y)，相对于图片左上角
    :param crop_width_px: 裁剪宽度（像素）
    :param auto_adjust: 当裁剪尺寸超出图片边界时是否自动调整，默认True
    :param supported_ratios: 支持的裁剪比例列表，默认["2.35_1", "1_1", "16_9"]
    :param return_format: 返回格式，可选值：
                        "all": 返回所有格式的裁剪参数
                        "pic_crop": 仅返回pic_crop_*格式参数
                        "crop_percent": 仅返回crop_percent_list格式参数
    :return: 裁剪参数字典，包含以下字段：
            - pic_crop_235_1: 2.35:1比例裁剪参数
            - pic_crop_1_1: 1:1比例裁剪参数
            - pic_crop_16_9: 16:9比例裁剪参数
            - crop_percent_list: 通用裁剪参数列表
    """
    # 默认支持的裁剪比例
    if supported_ratios is None:
        supported_ratios = ["2.35_1", "1_1", "16_9"]

    # 比例与高度/宽度比值映射
    ratio_mapping = {"2.35_1": 1 / 2.35, "1_1": 1, "16_9": 9 / 16}

    # 验证返回格式
    valid_formats = ["all", "pic_crop", "crop_percent"]
    if return_format not in valid_formats:
        raise ValueError(
            f"Invalid return_format: {return_format}. Must be one of: {valid_formats}"
        )

    # 验证裁剪比例
    for ratio in supported_ratios:
        if ratio not in ratio_mapping:
            raise ValueError(
                f"Unsupported ratio: {ratio}. Supported ratios: {list(ratio_mapping.keys())}"
            )

    try:
        # 加载图片
        image = load_image(image_source)

        # 获取图片原始尺寸
        original_width, original_height = image.size

        # 解析起点坐标
        start_x_px, start_y_px = start_point
        if start_x_px < 0 or start_y_px < 0:
            raise ValueError("Start point coordinates must be non-negative")

        # 初始化返回结果
        result = {}
        crop_percent_list = []

        # 为每个支持的比例生成裁剪参数
        for ratio in supported_ratios:
            ratio_hw = ratio_mapping[ratio]
            current_crop_width_px = crop_width_px
            crop_height_px = int(current_crop_width_px * ratio_hw)
            end_x_px = start_x_px + current_crop_width_px
            end_y_px = start_y_px + crop_height_px

            # 检查是否超出边界并处理
            if end_x_px > original_width or end_y_px > original_height:
                if auto_adjust:
                    # 优先调整宽度
                    if end_x_px > original_width:
                        current_crop_width_px = original_width - start_x_px
                        if current_crop_width_px < 0:
                            current_crop_width_px = 0
                        crop_height_px = int(current_crop_width_px * ratio_hw)
                        end_x_px = start_x_px + current_crop_width_px
                        end_y_px = start_y_px + crop_height_px

                    # 再调整高度
                    if end_y_px > original_height:
                        crop_height_px = original_height - start_y_px
                        if crop_height_px < 0:
                            crop_height_px = 0
                        current_crop_width_px = int(crop_height_px / ratio_hw)
                        end_x_px = start_x_px + current_crop_width_px
                        end_y_px = start_y_px + crop_height_px
                else:
                    raise ValueError(
                        f"Crop parameters exceed image boundaries for ratio {ratio}. "
                        f"Image size: {original_width}x{original_height}, "
                        f"Crop area: ({start_x_px}, {start_y_px}) to ({end_x_px}, {end_y_px})"
                    )

            # 计算归一化坐标
            x1 = start_x_px / original_width
            y1 = start_y_px / original_height
            x2 = end_x_px / original_width
            y2 = end_y_px / original_height

            # 确保坐标在有效范围内
            x1 = max(0.0, min(1.0, x1))
            y1 = max(0.0, min(1.0, y1))
            x2 = max(0.0, min(1.0, x2))
            y2 = max(0.0, min(1.0, y2))

            # 格式化为字符串，保留6位小数
            x1_str = f"{x1:.6f}"
            y1_str = f"{y1:.6f}"
            x2_str = f"{x2:.6f}"
            y2_str = f"{y2:.6f}"

            # 生成pic_crop_*格式参数
            if return_format in ["all", "pic_crop"]:
                result[f"pic_crop_{ratio.replace(':', '_')}"] = (
                    f"{x1_str}_{y1_str}_{x2_str}_{y2_str}"
                )

            # 生成crop_percent_list格式参数
            if return_format in ["all", "crop_percent"]:
                crop_percent_list.append(
                    {
                        "ratio": ratio,
                        "x1": float(x1_str),
                        "y1": float(y1_str),
                        "x2": float(x2_str),
                        "y2": float(y2_str),
                    }
                )

        # 添加crop_percent_list到结果
        if return_format in ["all", "crop_percent"]:
            result["crop_percent_list"] = crop_percent_list

        return result

    except FileNotFoundError as e:
        raise FileNotFoundError(f"Image file not found: {str(e)}")
    except ValueError:
        if not auto_adjust:
            raise
        return None
    except Exception as e:
        raise RuntimeError(f"Error processing image crop parameters: {str(e)}")


def generate_optimal_crop(
    image_source: Any, target_ratio: str, focus_point: Tuple[int, int] = None
) -> Dict[str, Any]:
    """
    为指定比例生成最优裁剪参数，基于图片中心或指定焦点

    :param image_source: 图片来源，同get_crop_parameters
    :param target_ratio: 目标裁剪比例，如"2.35_1", "1_1", "16_9"
    :param focus_point: 焦点坐标 (x, y)，默认使用图片中心
    :return: 裁剪参数字典
    """
    # 比例与高度/宽度比值映射
    ratio_mapping = {"2.35_1": 1 / 2.35, "1_1": 1, "16_9": 9 / 16}

    if target_ratio not in ratio_mapping:
        raise ValueError(
            f"Unsupported ratio: {target_ratio}. Supported ratios: {list(ratio_mapping.keys())}"
        )

    # 加载图片
    image = load_image(image_source)
    original_width, original_height = image.size

    # 确定焦点位置
    if focus_point is None:
        focus_x = original_width // 2
        focus_y = original_height // 2
    else:
        focus_x, focus_y = focus_point

    # 计算最优裁剪区域
    ratio_hw = ratio_mapping[target_ratio]

    # 尝试以焦点为中心进行裁剪
    if original_width / original_height > 1 / ratio_hw:
        # 图片比目标比例更宽，以高度为基准
        crop_height = original_height
        crop_width = int(crop_height * (1 / ratio_hw))
    else:
        # 图片比目标比例更高，以宽度为基准
        crop_width = original_width
        crop_height = int(crop_width * ratio_hw)

    # 计算裁剪起点
    start_x = max(0, focus_x - crop_width // 2)
    start_y = max(0, focus_y - crop_height // 2)

    # 确保裁剪区域不超出图片边界
    if start_x + crop_width > original_width:
        start_x = original_width - crop_width
    if start_y + crop_height > original_height:
        start_y = original_height - crop_height

    # 生成裁剪参数
    return get_crop_parameters(
        image_source=image_source,
        start_point=(start_x, start_y),
        crop_width_px=crop_width,
        supported_ratios=[target_ratio],
    )


def create_cover_info(crop_percent_list: list = None) -> Dict[str, Any]:
    """
    创建封面信息

    :param crop_percent_list: 封面裁剪信息
    :return: 封面信息
    """
    return {"crop_percent_list": crop_percent_list or []}


def create_image_list_item(image_media_id: str) -> Dict[str, Any]:
    """
    创建图片列表项

    :param image_media_id: 图片素材id
    :return: 图片列表项
    """
    return {"image_media_id": image_media_id}
