"""将工具 schema 转为 LangChain StructuredTool。"""

from __future__ import annotations

from typing import Any

from langchain_core.tools import StructuredTool
from pydantic import Field, create_model

from app.services.agent_registry import normalize_tool_ids
from app.services.agent_tool_schemas import _SCHEMAS
from app.services.chat_tools import execute_tool


def _json_type_to_python(spec: dict[str, Any]) -> type[Any]:
    t = spec.get("type")
    if t == "string":
        return str
    if t == "integer":
        return int
    if t == "number":
        return float
    if t == "boolean":
        return bool
    return Any


def _params_to_model(tool_id: str, parameters: dict[str, Any]) -> type[Any]:
    props = parameters.get("properties") or {}
    required = set(parameters.get("required") or [])
    fields: dict[str, Any] = {}
    for name, spec in props.items():
        if not isinstance(spec, dict):
            spec = {}
        py_type = _json_type_to_python(spec)
        desc = str(spec.get("description") or "")
        if name in required:
            fields[name] = (py_type, Field(..., description=desc))
        else:
            fields[name] = (py_type | None, Field(default=None, description=desc))
    model_name = f"Tool_{tool_id}_Args"
    if not fields:
        return create_model(model_name)
    return create_model(model_name, **fields)


def build_langchain_tools(tool_ids: list[str] | None) -> list[Any]:
    tools: list[Any] = []
    for tid in normalize_tool_ids(tool_ids):
        schema = _SCHEMAS.get(tid)
        if not schema:
            continue
        fn = schema.get("function") or {}
        name = str(fn.get("name") or tid)
        description = str(fn.get("description") or tid)
        parameters = fn.get("parameters") if isinstance(fn.get("parameters"), dict) else {}
        args_model = _params_to_model(tid, parameters)

        def _make_runner(tool_id: str):
            def _run(**kwargs: Any) -> str:
                clean = {k: v for k, v in kwargs.items() if v is not None}
                return execute_tool(tool_id, clean)

            return _run

        tools.append(
            StructuredTool.from_function(
                func=_make_runner(tid),
                name=name,
                description=description,
                args_schema=args_model,
            )
        )
    return tools