import os
import tempfile
from typing import Optional

from PIL import Image, ImageSequence
from nonebot.adapters import Event as BaseEvent
from nonebot.plugin import PluginMetadata
from nonebot_plugin_alconna import (
    Alconna,
    Args,
    Field,
    UniMessage,
    on_alconna,
)

__plugin_meta__ = PluginMetadata(
    name="ytpgif",
    description="生成来回镜像翻转的仿 YTPMV 动图。",
    usage="ytpgif [倍速=1.0] （倍速范围：0.1～20.0）",
    type="application",
    config=None,
    homepage=None,
)

# 参数定义
BASE_SEGMENT_DURATION = 0.25
BASE_INTERVAL = 0.25
MAX_SIZE = 256
MIN_SPEED = 0.1
MAX_SPEED = 20.0
MAX_FRAMES_PER_SEGMENT = 500

# 提示语
SPEED_TIPS = f"倍速必须是 {MIN_SPEED} 到 {MAX_SPEED} 之间的数字"


# 定义命令 + 参数校验
ytpgif_cmd = on_alconna(
    Alconna(
        "ytpgif",
        Args[
            "speed?",
            float,
            Field(
                default=1.0,
                unmatch_tips=lambda x: f"“{x}”不是有效数值。{SPEED_TIPS}",
            ),
        ],
    ),
    use_cmd_start=True,
    use_cmd_sep=False,
    skip_for_unmatch=False,
)


async def get_image_url(event: BaseEvent) -> Optional[str]:
    """从事件中提取图片 URL，支持直接消息和回复"""
    msg = event.get_message()
    for seg in msg:
        if seg.type == "image" and seg.data.get("url"):
            return str(seg.data["url"])

    if hasattr(event, "reply") and (reply := event.reply):
        reply_msg = reply.message
        for seg in reply_msg:
            if seg.type == "image" and seg.data.get("url"):
                return str(seg.data["url"])
    return None


async def download_image(url: str) -> bytes:
    import httpx
    async with httpx.AsyncClient() as client:
        resp = await client.get(url, timeout=10)
        resp.raise_for_status()
        return resp.content


def resize_frame(frame: Image.Image) -> Image.Image:
    """缩放图像，保持宽高比，不超过 MAX_SIZE"""
    w, h = frame.size
    if w <= MAX_SIZE and h <= MAX_SIZE:
        return frame

    scale = MAX_SIZE / max(w, h)
    new_w = int(w * scale)
    new_h = int(h * scale)
    return frame.resize((new_w, new_h), Image.Resampling.LANCZOS)


@ytpgif_cmd.handle()
async def handle_ytpgif(event: BaseEvent, speed: float = 1.0):
    # === 校验 speed 范围 ===
    if not (MIN_SPEED <= speed <= MAX_SPEED):
        await ytpgif_cmd.send(
            await UniMessage.text(f"❌ {SPEED_TIPS}").export()
        )
        return

    img_url = await get_image_url(event)
    if not img_url:
        await ytpgif_cmd.send(
            await UniMessage.text(
                "请发送一张图片或回复一张图片来生成镜像动图。"
            ).export()
        )
        return

    try:
        image_data = await download_image(img_url)
    except Exception as e:
        print(f"[YTPGIF] 下载失败: {e}")
        await ytpgif_cmd.send(
            await UniMessage.text("❌ 图片下载失败，请重试。").export()
        )
        return

    input_path = output_path = None
    try:
        with tempfile.NamedTemporaryFile(delete=False, suffix=".gif") as tmp_in:
            tmp_in.write(image_data)
            input_path = tmp_in.name

        with tempfile.NamedTemporaryFile(delete=False, suffix=".gif") as tmp_out:
            output_path = tmp_out.name

        with Image.open(input_path) as src_img:
            # === 判断是否为动图 ===
            try:
                n_frames = getattr(src_img, "n_frames", 1)
                is_animated = n_frames > 1
            except Exception:
                is_animated = False

            output_frames = []
            output_durations_ms = []

            if is_animated:
                # === 动图模式：截取正向 + 镜像两段 ===
                frames_with_duration = []
                palette = src_img.getpalette()

                for idx in range(n_frames):
                    src_img.seek(idx)
                    frame = src_img.copy()
                    # 检查是否需要透明通道
                    has_alpha = (
                        frame.mode in ("RGBA", "LA")
                        or (frame.mode == "P" and "transparency" in frame.info)
                    )
                    if has_alpha:
                        frame = frame.convert("RGBA")
                    else:
                        frame = frame.convert("RGB")
                    resized_frame = resize_frame(frame)

                    # 若原图有调色板，尝试保留（可选）
                    if palette and resized_frame.mode == "P":
                        try:
                            resized_frame.putpalette(palette)
                        except Exception:  # noqa
                            pass

                    ms = frame.info.get("duration", int(BASE_SEGMENT_DURATION * 1000))
                    dur_sec = max(0.01, ms / 1000.0)
                    frames_with_duration.append((resized_frame, dur_sec))

                max_dur = BASE_SEGMENT_DURATION * speed
                accumulated = 0.0
                frame_count = 0

                # 正向段
                for img, dur in frames_with_duration:
                    if accumulated + dur > max_dur or frame_count >= MAX_FRAMES_PER_SEGMENT:
                        break
                    output_frames.append(img)
                    output_durations_ms.append(int(dur * 1000))
                    accumulated += dur
                    frame_count += 1

                if frame_count == 0:
                    await ytpgif_cmd.send(
                        await UniMessage.text("动图帧太短，无法生成有效片段。").export()
                    )
                    return

                # 镜像段（从头开始）
                accumulated = 0.0
                frame_count = 0
                for img, dur in frames_with_duration:
                    if accumulated + dur > max_dur or frame_count >= MAX_FRAMES_PER_SEGMENT:
                        break
                    flipped = img.transpose(Image.FLIP_LEFT_RIGHT)
                    output_frames.append(flipped)
                    output_durations_ms.append(int(dur * 1000))
                    accumulated += dur
                    frame_count += 1

            else:
                # === 静态图模式：制作翻转动画 ===
                raw_frame = src_img.convert("RGBA")
                resized_frame = resize_frame(raw_frame)

                interval_sec = max(0.025, min(2.5, BASE_INTERVAL / speed))
                duration_ms = int(interval_sec * 1000)

                frame1 = resized_frame
                frame2 = resized_frame.transpose(Image.FLIP_LEFT_RIGHT)

                output_frames = [frame1, frame2]
                output_durations_ms = [duration_ms, duration_ms]

            if len(output_frames) < 1:
                await ytpgif_cmd.send(
                    await UniMessage.text("未能生成任何帧。").export()
                )
                return

            # === 🔐 关键修复：防止无透明图的颜色被当成透明 ===
            need_transparency = False
            for frame in output_frames:
                if frame.mode == "RGBA":
                    alpha_channel = frame.getchannel("A")
                    if any(pix < 255 for pix in alpha_channel.getdata()):
                        need_transparency = True
                        break
                elif frame.mode == "P" and "transparency" in frame.info:
                    need_transparency = True
                    break

            # 如果不需要透明，则统一转为 RGB 避免调色板污染
            if not need_transparency:
                output_frames = [f.convert("RGB") for f in output_frames]

            # 构建保存参数
            save_kwargs = {
                "save_all": True,
                "append_images": output_frames[1:],
                "format": "GIF",
                "loop": 0,           # 无限循环
                "duration": output_durations_ms,
                "disposal": 2,       # 清除到背景色，避免残留
                "optimize": False,   # 关闭抖动（等效 -dither none）
            }

            # 只有真正需要透明时才启用 transparency
            if need_transparency:
                save_kwargs["transparency"] = 0

            output_frames[0].save(output_path, **save_kwargs)

        # 发送结果
        with open(output_path, "rb") as f:
            result_image = UniMessage.image(raw=f.read())
        await ytpgif_cmd.send(await result_image.export())

    except Exception as e:
        print(f"[YTPGIF] 处理失败: {e}")
        await ytpgif_cmd.send(
            await UniMessage.text("❌ 处理失败，可能是图片格式不支持、文件损坏或过大。").export()
        )
    finally:
        for path in filter(None, [input_path, output_path]):
            if os.path.exists(path):
                try:
                    os.unlink(path)
                except:  # noqa
                    pass