from __future__ import annotations

import re
import time
from pathlib import Path
from typing import TYPE_CHECKING, Any

from aicd.agent.tool_args_coerce import coerce_tool_json_array
from aicd.config import effective_llm_config

if TYPE_CHECKING:
    from aicd.agent.tool_router import ToolRouter


def _safe_report_stem(name: str) -> str:
    s = re.sub(r"[^\w\-]+", "_", name.strip(), flags=re.ASCII)
    return (s[:80] if s else "vision") or "vision"


async def _vision_analyze_image(
    router: "ToolRouter", args: dict[str, Any]
) -> dict[str, Any]:
    llm = router._vision_llm
    fn = getattr(llm, "chat_with_images", None)
    if llm is None or not callable(fn):
        return {
            "ok": False,
            "error": "vision LLM not configured (internal error)",
        }

    prompt = str(args.get("prompt") or "").strip()
    if not prompt:
        return {"ok": False, "error": "prompt is required"}

    raw_paths: list[str] = []
    ip = args.get("image_path")
    if ip is not None and str(ip).strip():
        raw_paths.append(str(ip).strip())
    ips = args.get("image_paths")
    if ips is not None and not isinstance(ips, list):
        co = coerce_tool_json_array(ips, "image_paths", allow_empty=True)
        if isinstance(co, dict):
            return co
        ips = co
    if isinstance(ips, list):
        raw_paths.extend(str(x).strip() for x in ips if str(x).strip())
    if not raw_paths:
        return {"ok": False, "error": "provide image_path and/or image_paths[]"}

    resolved: list[Path] = []
    for rp in raw_paths[:10]:
        resolved.append(router._resolve_path(rp))
    allowed_ext = {".png", ".jpg", ".jpeg", ".webp", ".gif"}
    for p in resolved:
        if not p.is_file():
            return {"ok": False, "error": f"not a file: {p}"}
        if p.suffix.lower() not in allowed_ext:
            return {
                "ok": False,
                "error": f"unsupported type {p.suffix!r}; use png/jpg/jpeg/webp/gif",
            }

    eff = effective_llm_config(router._cfg.llm)
    model_used = (router._cfg.llm.vision_model or eff.model).strip()

    try:
        raw_vision = await fn(prompt, resolved)
    except Exception as e:  # noqa: BLE001
        return {"ok": False, "error": repr(e)}

    if isinstance(raw_vision, dict):
        analysis = str(raw_vision.get("analysis") or raw_vision.get("text") or "").strip()
        per_image = raw_vision.get("per_image")
        if not isinstance(per_image, list):
            per_image = []
    else:
        analysis = str(raw_vision).strip()
        per_image = []

    out: dict[str, Any] = {
        "ok": True,
        "analysis": analysis,
        "image_paths": [str(p) for p in resolved],
        "per_image": per_image,
        "model_used": model_used,
        "word_insert_note": (
            "插入 Word 时请使用 **image_paths** 上的**原始文件**（非 API 缩放版）。"
            "若根据本分析中的像素坐标裁剪，应对原图调用 **image_crop**，并设置 "
            "**bbox_reference_width** / **bbox_reference_height** 为该图对应的 **sent_width** / **sent_height**（见 per_image）。"
        ),
    }

    if bool(args.get("save_report", True)):
        router._ai_tmp.mkdir(parents=True, exist_ok=True)
        rn = args.get("report_name")
        if rn is not None and str(rn).strip():
            base = _safe_report_stem(str(rn))
        else:
            base = (
                _safe_report_stem(resolved[0].stem)
                + "_"
                + time.strftime("%Y%m%d_%H%M%S")
            )
        report_path = router._ai_tmp / f"{base}.md"
        header = (
            f"# Vision analysis\n\n"
            f"**Images:** {', '.join(str(p) for p in resolved)}\n\n"
            f"**Prompt:** {prompt}\n\n"
            f"**Model:** {model_used}\n\n---\n\n"
        )
        report_path.write_text(header + analysis + "\n", encoding="utf-8")
        out["report_path"] = str(report_path)
        out["note"] = (
            "Report saved under AI tmp; use fs_read_file on report_path for follow-up."
        )

    return out


HANDLERS: dict[str, Any] = {
    "vision_analyze_image": _vision_analyze_image,
}
