from __future__ import annotations

import asyncio
import base64
import io
from pathlib import Path
from unittest.mock import AsyncMock, MagicMock

from PIL import Image


def test_vision_analyze_image_tool_mock(tmp_path: Path) -> None:
    from aicd.runtime import build_runtime

    async def _go() -> None:
        ctx = await build_runtime(home=tmp_path / "home")
        ok, _ = ctx.set_ai_workspace(str(tmp_path))
        assert ok is True

        img = tmp_path / "plot.png"
        Image.new("RGB", (32, 24), color=(40, 80, 120)).save(img)
        img_res = str(img.resolve())

        mock_llm = MagicMock()
        mock_llm.chat_with_images = AsyncMock(
            return_value={
                "analysis": "Trend: sales increase Q1–Q3.",
                "per_image": [
                    {
                        "path": img_res,
                        "source_width": 32,
                        "source_height": 24,
                        "sent_width": 32,
                        "sent_height": 24,
                    }
                ],
            }
        )
        ctx.router.set_vision_llm(mock_llm)

        r = await ctx.router.run(
            "vision_analyze_image",
            {
                "prompt": "Describe the chart trend.",
                "image_path": str(img),
                "save_report": True,
            },
        )
        assert r.get("ok") is True
        assert "Q1" in r.get("analysis", "")
        assert isinstance(r.get("per_image"), list) and len(r["per_image"]) == 1
        assert "原始文件" in (r.get("word_insert_note") or "")
        assert r.get("report_path")
        rp = Path(r["report_path"])
        assert rp.is_file()
        assert "Trend:" in rp.read_text(encoding="utf-8")
        mock_llm.chat_with_images.assert_awaited_once()
        await ctx.conn.close()

    asyncio.run(_go())


def test_image_file_to_data_url_png(tmp_path: Path) -> None:
    from aicd.agent.llm import image_file_to_data_url

    img = tmp_path / "a.png"
    Image.new("RGBA", (4, 4), color=(255, 0, 0, 128)).save(img)
    url = image_file_to_data_url(img)
    assert url.startswith("data:image/png;base64,")
    assert len(url) > 100


def test_image_file_to_data_url_long_edge_1920(tmp_path: Path) -> None:
    from aicd.agent.llm import image_file_to_data_url

    img = tmp_path / "wide.png"
    Image.new("RGB", (2600, 900), color=(10, 20, 30)).save(img)
    url = image_file_to_data_url(img)
    raw_png = base64.standard_b64decode(url.split(",", 1)[1])
    out = Image.open(io.BytesIO(raw_png))
    assert max(out.size) <= 1920


def test_image_file_to_data_url_large_source_compressed(tmp_path: Path) -> None:
    from aicd.agent.llm import image_file_to_data_url

    img = tmp_path / "noisy.png"
    # 噪声图 PNG 往往 > 1MiB，触发「源 ≥1M 压缩」与编码后 ≤1M 迭代
    Image.effect_noise((1800, 1800), 12).convert("RGB").save(img)
    assert img.stat().st_size >= 1024 * 1024
    url = image_file_to_data_url(img)
    raw_png = base64.standard_b64decode(url.split(",", 1)[1])
    assert len(raw_png) <= 1024 * 1024
    out = Image.open(io.BytesIO(raw_png))
    assert max(out.size) <= 1920
