import inspect
from dataclasses import dataclass
from typing import Any, Callable, Dict, Optional

from animuz_core.genai.tools import InputSchema, Property, OpenAITool, AnthropicTool


@dataclass
class ToolSpec:
    name: str
    description: str
    parameters: Dict[str, Any]
    fn: Callable[..., Any]


def _infer_parameters(fn: Callable[..., Any]) -> Dict[str, Any]:
    signature = inspect.signature(fn)
    properties: Dict[str, Dict[str, str]] = {}
    required = []

    type_map = {
        str: "string",
        int: "integer",
        float: "number",
        bool: "boolean",
    }

    for param in signature.parameters.values():
        if param.kind in (inspect.Parameter.VAR_KEYWORD, inspect.Parameter.VAR_POSITIONAL):
            continue

        annotation = param.annotation
        json_type = type_map.get(annotation, "string")
        properties[param.name] = {
            "type": json_type,
            "description": "",
        }

        if param.default is inspect.Parameter.empty:
            required.append(param.name)

    return {
        "type": "object",
        "properties": properties,
        "required": required,
    }


def tool(
    fn: Optional[Callable[..., Any]] = None,
    *,
    name: Optional[str] = None,
    description: Optional[str] = None,
    parameters: Optional[Dict[str, Any]] = None,
):
    def wrap(func: Callable[..., Any]) -> ToolSpec:
        return ToolSpec(
            name=name or func.__name__,
            description=description or (func.__doc__ or ""),
            parameters=parameters or _infer_parameters(func),
            fn=func,
        )

    return wrap if fn is None else wrap(fn)


def _schema_to_input_schema(schema: Dict[str, Any]) -> InputSchema:
    properties = {
        key: Property(type=value.get("type", "string"), description=value.get("description", ""))
        for key, value in schema.get("properties", {}).items()
    }
    return InputSchema(
        type=schema.get("type", "object"),
        properties=properties,
        required=schema.get("required", []),
    )


def build_openai_tool(spec: ToolSpec) -> OpenAITool:
    return OpenAITool(
        name=spec.name,
        description=spec.description,
        parameters=_schema_to_input_schema(spec.parameters),
    )


def build_anthropic_tool(spec: ToolSpec) -> AnthropicTool:
    return AnthropicTool(
        name=spec.name,
        description=spec.description,
        input_schema=_schema_to_input_schema(spec.parameters),
    )
