from __future__ import annotations

from typing import TYPE_CHECKING, Any

from aicd.agent.tool_args_coerce import coerce_tool_json_array, coerce_tool_json_object
from aicd.tools.image_crop import crop_raster_image
from aicd.tools.image_transform import list_image_operations, operations_help, run_image_transform

if TYPE_CHECKING:
    from aicd.agent.tool_router import ToolRouter


def _resolve_image_params_paths(router: ToolRouter, params: dict[str, Any]) -> tuple[dict[str, Any], str | None]:
    q = dict(params)
    for key in ("overlay_path", "second_path"):
        v = q.get(key)
        if v:
            q[key] = str(router._resolve_path(str(v)))
    paths = q.get("paths")
    if isinstance(paths, str) and paths.strip():
        co = coerce_tool_json_array(paths, "params.paths", allow_empty=True)
        if isinstance(co, dict):
            return {}, str(co.get("error", "params.paths invalid"))
        paths = co
    if isinstance(paths, list):
        q["paths"] = [
            str(router._resolve_path(str(x))) for x in paths if str(x).strip()
        ]
    elif paths is not None and not isinstance(paths, list):
        return {}, "params.paths must be array or JSON array string"
    return q, None


def _image_transform(router: ToolRouter, args: dict[str, Any]) -> dict[str, Any]:
    op = str(args.get("operation") or "").strip().lower().replace("-", "_")
    if op in ("help", "list_operations", "operations_help"):
        return {
            "ok": True,
            "operations_help": operations_help(),
            "known_operations": list_image_operations(),
        }
    outp_s = args.get("output_path")
    if not outp_s or not str(outp_s).strip():
        return {"ok": False, "error": "output_path is required"}
    out = router._resolve_path(str(outp_s).strip())
    inp_raw = args.get("input_path")
    if op == "solid_canvas" and (inp_raw is None or not str(inp_raw).strip()):
        inp = out.parent
    else:
        if inp_raw is None or not str(inp_raw).strip():
            return {
                "ok": False,
                "error": "input_path is required (optional only for solid_canvas)",
            }
        inp = router._resolve_path(str(inp_raw).strip())
    p_obj, p_err = coerce_tool_json_object(args.get("params"), "params")
    if p_err:
        return {"ok": False, "error": p_err}
    params, rp_err = _resolve_image_params_paths(router, p_obj)
    if rp_err:
        return {"ok": False, "error": rp_err}
    return run_image_transform(
        op,
        inp,
        out,
        params=params,
        policy_check=router._policy.check_allowed,
    )


def _image_crop(router: ToolRouter, args: dict[str, Any]) -> dict[str, Any]:
    inp = router._resolve_path(str(args["input_path"]))
    out = router._resolve_path(str(args["output_path"]))
    brw = args.get("bbox_reference_width")
    brh = args.get("bbox_reference_height")
    bbox_rw = int(brw) if brw is not None and str(brw).strip() != "" else None
    bbox_rh = int(brh) if brh is not None and str(brh).strip() != "" else None
    if (bbox_rw is None) ^ (bbox_rh is None):
        return {
            "ok": False,
            "error": "bbox_reference_width and bbox_reference_height must be set together",
        }
    return crop_raster_image(
        inp,
        out,
        left=int(args["left"]),
        top=int(args["top"]),
        width=int(args["width"]),
        height=int(args["height"]),
        policy_check=router._policy.check_allowed,
        bbox_reference_width=bbox_rw,
        bbox_reference_height=bbox_rh,
    )


HANDLERS: dict[str, Any] = {
    "image_transform": _image_transform,
    "image_crop": _image_crop,
}
