"""
工具入参健壮性：部分模型/网关会把 array/object 序列化成 JSON 字符串。

统一在此解析，避免 ``list(string)`` 按字符拆分等隐性错误。
"""

from __future__ import annotations

import json
from typing import Any


def coerce_tool_json_array(
    raw: Any,
    field: str,
    *,
    allow_empty: bool = True,
) -> list[Any] | dict[str, Any]:
    """
    解析应为 JSON 数组的参数。

    成功返回 ``list``；失败返回 ``{"ok": False, "error": "..."}``。
    """
    if isinstance(raw, list):
        if not allow_empty and len(raw) == 0:
            return {"ok": False, "error": f"{field} must be a non-empty array"}
        return raw
    if isinstance(raw, str):
        s = raw.strip()
        if not s:
            if not allow_empty:
                return {"ok": False, "error": f"{field} must be a non-empty array or JSON array string"}
            return []
        try:
            parsed = json.loads(s)
        except json.JSONDecodeError as e:
            return {
                "ok": False,
                "error": f"{field} must be array or valid JSON array string: {e}",
            }
        if not isinstance(parsed, list):
            return {
                "ok": False,
                "error": f"{field} JSON string must decode to an array, got {type(parsed).__name__}",
            }
        if not allow_empty and len(parsed) == 0:
            return {"ok": False, "error": f"{field} must be a non-empty array"}
        return parsed
    if raw is None:
        return {"ok": False, "error": f"{field} is required (array)"}
    return {"ok": False, "error": f"{field} must be array"}


def coerce_tool_json_object(
    raw: Any,
    field: str,
) -> tuple[dict[str, Any], str | None]:
    """
    解析应为 JSON 对象的参数（含字符串形式的 JSON）。

    ``raw is None`` 视为无对象，返回 ``({}, None)``（如省略的 ``params``）。
    返回 ``(obj, None)`` 或 ``({}, error_message)``。
    """
    if raw is None:
        return {}, None
    if isinstance(raw, dict):
        return raw, None
    if isinstance(raw, str):
        s = raw.strip()
        if not s:
            return {}, None
        try:
            parsed = json.loads(s)
        except json.JSONDecodeError as e:
            return {}, f"{field} must be object or valid JSON object string: {e}"
        if not isinstance(parsed, dict):
            return (
                {},
                f"{field} JSON string must decode to an object, got {type(parsed).__name__}",
            )
        return parsed, None
    return {}, f"{field} must be object"


def coerce_tool_json_block_object(
    raw: Any, field: str, index: int
) -> tuple[dict[str, Any], str | None]:
    """
    数组元素有时被序列化成 JSON 对象字符串（如 ``docx_compose`` 的 ``blocks[i]``）。

    返回 ``(dict, None)`` 或 ``({}, error_message)``。
    """
    if isinstance(raw, dict):
        return raw, None
    if isinstance(raw, str):
        s = raw.strip()
        if not s:
            return {}, f"{field}[{index}] must be object, got empty string"
        try:
            parsed = json.loads(s)
        except json.JSONDecodeError as e:
            return {}, f"{field}[{index}] must be object or valid JSON object string: {e}"
        if not isinstance(parsed, dict):
            return (
                {},
                f"{field}[{index}] JSON must decode to object, got {type(parsed).__name__}",
            )
        return parsed, None
    return {}, f"{field}[{index}] must be object"
