"""图片信息查看 MCP 服务器。

支持查看各种图片格式的元数据信息，包括：
- 基本信息（尺寸、格式、颜色模式等）
- EXIF 数据（拍摄设备、日期、GPS等）
- GIF 动画信息（帧数、时长、循环次数等）
"""

from __future__ import annotations

import logging
import sys
from datetime import datetime
from pathlib import Path
from typing import Any

from mcp.server.fastmcp import FastMCP
from PIL import Image, ImageSequence
from PIL.ExifTags import TAGS, GPSTAGS

# 将日志发送到 stderr，避免干扰 MCP stdio
logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s %(levelname)s %(name)s: %(message)s",
    stream=sys.stderr,
)
logger = logging.getLogger(__name__)

mcp = FastMCP("image-info")

DEFAULT_FRAME_DURATION_MS = 100

# 支持的图片格式
SUPPORTED_FORMATS = {
    "JPEG", "JPG", "PNG", "GIF", "WEBP", "TIFF", "BMP", "ICO", "HEIC", "HEIF"
}


def _normalize_path(file_path: str) -> Path:
    """将任意路径字符串转换成可用的 Path 并做存在性检查。"""
    path = Path(file_path).expanduser()
    if not path.exists():
        raise FileNotFoundError(f"找不到文件：{path}")
    if not path.is_file():
        raise IsADirectoryError(f"路径不是文件：{path}")
    return path


def _collect_frame_durations(image: Image.Image) -> list[int]:
    """遍历 GIF 并返回每帧时长（毫秒）。"""
    durations: list[int] = []
    base_duration = int(image.info.get("duration", DEFAULT_FRAME_DURATION_MS) or DEFAULT_FRAME_DURATION_MS)
    for frame in ImageSequence.Iterator(image):
        duration = int(frame.info.get("duration", base_duration) or base_duration)
        if duration <= 0:
            duration = DEFAULT_FRAME_DURATION_MS
        durations.append(duration)

    if not durations:
        durations.append(base_duration)
    return durations


def _playback_mode(loop_value: int) -> str:
    """根据 loop 参数给出易懂的播放模式描述。"""
    if loop_value == 0:
        return "infinite_loop"
    if loop_value == 1:
        return "single_play"
    return "finite_loop"


def _get_gif_info(image: Image.Image, path: Path) -> dict[str, Any]:
    """获取 GIF 动画相关信息。"""
    frame_durations = _collect_frame_durations(image)
    total_duration_ms = sum(frame_durations)
    loop_raw = int(image.info.get("loop", 1))
    playback = _playback_mode(loop_raw)

    return {
        "is_animated": True,
        "frame_count": getattr(image, "n_frames", len(frame_durations)),
        "frame_durations_ms": frame_durations,
        "total_duration_ms": total_duration_ms,
        "total_duration_seconds": round(total_duration_ms / 1000, 3),
        "loop_count_raw": loop_raw,
        "playback_mode": playback,
    }


def _convert_exif_value(value: Any) -> Any:
    """将 EXIF 值转换为可序列化的格式。"""
    if isinstance(value, bytes):
        try:
            return value.decode("utf-8", errors="ignore")
        except Exception:
            return str(value)
    elif isinstance(value, tuple):
        return [_convert_exif_value(v) for v in value]
    elif hasattr(value, "numerator") and hasattr(value, "denominator"):
        # 处理 Fraction/Ratio 类型
        if value.denominator != 0:
            return float(value.numerator) / float(value.denominator)
        return str(value)
    elif isinstance(value, datetime):
        return value.isoformat()
    return value


def _parse_gps_coordinate(coord: tuple, ref: str) -> float | None:
    """解析 GPS 坐标。"""
    try:
        degrees = float(coord[0])
        minutes = float(coord[1])
        seconds = float(coord[2])
        result = degrees + minutes / 60 + seconds / 3600
        if ref in ("S", "W"):
            result = -result
        return round(result, 6)
    except Exception:
        return None


def _get_exif_info(image: Image.Image) -> dict[str, Any] | None:
    """提取 EXIF 信息。"""
    try:
        exif_data = image._getexif()
        if not exif_data:
            return None
    except (AttributeError, Exception):
        return None

    exif_info: dict[str, Any] = {}
    gps_info: dict[str, Any] = {}

    for tag_id, value in exif_data.items():
        tag_name = TAGS.get(tag_id, str(tag_id))

        if tag_name == "GPSInfo":
            # 解析 GPS 信息
            for gps_tag_id, gps_value in value.items():
                gps_tag_name = GPSTAGS.get(gps_tag_id, str(gps_tag_id))
                gps_info[gps_tag_name] = _convert_exif_value(gps_value)

            # 计算经纬度
            if "GPSLatitude" in gps_info and "GPSLatitudeRef" in gps_info:
                lat = _parse_gps_coordinate(
                    value.get(2), gps_info.get("GPSLatitudeRef", "N")
                )
                if lat is not None:
                    gps_info["latitude"] = lat

            if "GPSLongitude" in gps_info and "GPSLongitudeRef" in gps_info:
                lon = _parse_gps_coordinate(
                    value.get(4), gps_info.get("GPSLongitudeRef", "E")
                )
                if lon is not None:
                    gps_info["longitude"] = lon
        else:
            exif_info[tag_name] = _convert_exif_value(value)

    result: dict[str, Any] = {}

    # 提取常用字段
    common_fields = {
        "Make": "camera_make",
        "Model": "camera_model",
        "DateTime": "date_time",
        "DateTimeOriginal": "date_time_original",
        "ExposureTime": "exposure_time",
        "FNumber": "f_number",
        "ISOSpeedRatings": "iso",
        "FocalLength": "focal_length",
        "Flash": "flash",
        "Orientation": "orientation",
        "Software": "software",
        "Artist": "artist",
        "Copyright": "copyright",
        "ImageDescription": "description",
    }

    for exif_key, result_key in common_fields.items():
        if exif_key in exif_info:
            result[result_key] = exif_info[exif_key]

    if gps_info:
        result["gps"] = gps_info

    # 如果有其他信息，也包含进来
    if exif_info:
        result["all_exif"] = exif_info

    return result if result else None


def _get_png_info(image: Image.Image) -> dict[str, Any] | None:
    """获取 PNG 特有的文本块信息。"""
    png_info: dict[str, Any] = {}

    # PNG 文本块
    text_chunks = {}
    for key in ["Title", "Author", "Description", "Copyright", "Creation Time",
                "Software", "Disclaimer", "Warning", "Source", "Comment"]:
        if key in image.info:
            text_chunks[key.lower().replace(" ", "_")] = image.info[key]

    if text_chunks:
        png_info["text_chunks"] = text_chunks

    # 其他 PNG 信息
    if "dpi" in image.info:
        png_info["dpi"] = image.info["dpi"]
    if "gamma" in image.info:
        png_info["gamma"] = image.info["gamma"]

    return png_info if png_info else None


def _get_webp_info(image: Image.Image) -> dict[str, Any] | None:
    """获取 WebP 特有信息。"""
    webp_info: dict[str, Any] = {}

    # 检查是否为动画 WebP
    n_frames = getattr(image, "n_frames", 1)
    if n_frames > 1:
        webp_info["is_animated"] = True
        webp_info["frame_count"] = n_frames

        # 尝试获取帧时长
        durations = []
        for i in range(n_frames):
            try:
                image.seek(i)
                duration = image.info.get("duration", 100)
                durations.append(duration)
            except EOFError:
                break

        if durations:
            webp_info["frame_durations_ms"] = durations
            webp_info["total_duration_ms"] = sum(durations)
            webp_info["total_duration_seconds"] = round(sum(durations) / 1000, 3)

    return webp_info if webp_info else None


@mcp.tool()
async def get_image_info(file_path: str) -> dict[str, Any]:
    """
    获取图片的详细信息。

    支持的格式：JPEG、PNG、GIF、WebP、TIFF、BMP、ICO 等。

    返回信息包括：
    - 基本信息：文件路径、大小、尺寸、格式、颜色模式
    - EXIF 数据：拍摄设备、日期、曝光参数、GPS 等（仅 JPEG/TIFF）
    - 动画信息：帧数、时长、循环次数等（仅 GIF/WebP 动画）
    - 格式特有信息：PNG 文本块等

    Args:
        file_path: 图片文件路径

    Returns:
        包含图片各类元数据的字典
    """
    image_path = _normalize_path(file_path)

    with Image.open(image_path) as image:
        image_format = image.format or "UNKNOWN"

        # 基本信息
        info: dict[str, Any] = {
            "file_path": str(image_path),
            "file_name": image_path.name,
            "file_size_bytes": image_path.stat().st_size,
            "format": image_format,
            "size": {
                "width": image.width,
                "height": image.height,
            },
            "color_mode": image.mode,
            "has_transparency": image.mode in {"RGBA", "LA", "PA"} or "transparency" in image.info,
        }

        # 添加 DPI 信息（如果有）
        if "dpi" in image.info:
            info["dpi"] = image.info["dpi"]

        # 根据格式添加特定信息
        if image_format == "GIF":
            gif_info = _get_gif_info(image, image_path)
            info["animation"] = gif_info

        elif image_format in {"JPEG", "JPG", "TIFF"}:
            exif_info = _get_exif_info(image)
            if exif_info:
                info["exif"] = exif_info

        elif image_format == "PNG":
            png_info = _get_png_info(image)
            if png_info:
                info["png_info"] = png_info

        elif image_format == "WEBP":
            webp_info = _get_webp_info(image)
            if webp_info:
                info["animation"] = webp_info

            # WebP 也可能包含 EXIF
            exif_info = _get_exif_info(image)
            if exif_info:
                info["exif"] = exif_info

    logger.info("读取图片信息完成：%s", image_path)
    return info


def main() -> None:
    """启动图片信息查看 MCP 服务器（stdio 模式）。"""
    logger.info("启动图片信息查看 MCP 服务器 (stdio transport)")
    mcp.run(transport="stdio")


if __name__ == "__main__":
    main()
