"""
Tool registry — defines ToolDefinition and the BUILTIN_TOOLS catalog.

ToolDefinition:
  A Pydantic model describing a tool's name, description, JSON Schema
  parameters, whether it requires user confirmation, and its async handler.

BUILTIN_TOOLS:
  The four built-in tools available to all agents: web_search, kb_search,
  http_request, calendar_lookup.

Usage:
    from orchestrator.tools.registry import BUILTIN_TOOLS, get_tools_for_agent, to_litellm_format

    agent_tools = get_tools_for_agent(agent)
    litellm_tools = to_litellm_format(agent_tools)
"""

from __future__ import annotations

from typing import Any, Callable

from pydantic import BaseModel, ConfigDict


class ToolDefinition(BaseModel):
    """
    Describes a tool that an agent can invoke.

    Attributes:
        name:                  Unique tool identifier (snake_case).
        description:           Human-readable description for LLM function calling.
        parameters:            JSON Schema object defining accepted arguments.
        requires_confirmation: If True, pause and ask user before executing.
        handler:               Async callable that executes the tool.
                               Excluded from serialization.
    """

    model_config = ConfigDict(arbitrary_types_allowed=True)

    name: str
    description: str
    parameters: dict[str, Any]
    requires_confirmation: bool = False
    handler: Callable[..., Any]

    model_config = ConfigDict(
        arbitrary_types_allowed=True,
        # Exclude handler from .model_dump() / .model_json_schema()
        # since callables are not JSON-serializable
    )

    def model_dump(self, **kwargs: Any) -> dict[str, Any]:
        """Exclude handler from dict output."""
        kwargs.setdefault("exclude", set())
        if isinstance(kwargs["exclude"], set):
            kwargs["exclude"] = kwargs["exclude"] | {"handler"}
        return super().model_dump(**kwargs)


# ---------------------------------------------------------------------------
# Import builtin handlers (lazy — avoids import errors for missing optional deps)
# ---------------------------------------------------------------------------

from orchestrator.tools.builtins.web_search import web_search as _web_search_handler
from orchestrator.tools.builtins.kb_search import kb_search as _kb_search_handler
from orchestrator.tools.builtins.http_request import http_request as _http_request_handler
from orchestrator.tools.builtins.calendar_lookup import calendar_lookup as _calendar_lookup_handler


# ---------------------------------------------------------------------------
# BUILTIN_TOOLS — the four built-in tools
# ---------------------------------------------------------------------------

BUILTIN_TOOLS: dict[str, ToolDefinition] = {
    "web_search": ToolDefinition(
        name="web_search",
        description=(
            "Search the web using Brave Search and return the top results. "
            "Use this to find current information, news, or facts not in your training data."
        ),
        parameters={
            "type": "object",
            "properties": {
                "query": {
                    "type": "string",
                    "description": "The search query string.",
                }
            },
            "required": ["query"],
        },
        requires_confirmation=False,
        handler=_web_search_handler,
    ),
    "kb_search": ToolDefinition(
        name="kb_search",
        description=(
            "Search the tenant's knowledge base using semantic similarity. "
            "Use this to find relevant internal documentation, policies, or FAQs."
        ),
        parameters={
            "type": "object",
            "properties": {
                "query": {
                    "type": "string",
                    "description": "The search query to find relevant knowledge base content.",
                }
            },
            "required": ["query"],
        },
        requires_confirmation=False,
        handler=_kb_search_handler,
    ),
    "http_request": ToolDefinition(
        name="http_request",
        description=(
            "Make an outbound HTTP request to an external API or URL. "
            "Supports GET, POST, PUT, DELETE. Response is capped at 1MB."
        ),
        parameters={
            "type": "object",
            "properties": {
                "url": {
                    "type": "string",
                    "description": "The target URL.",
                },
                "method": {
                    "type": "string",
                    "enum": ["GET", "POST", "PUT", "DELETE"],
                    "description": "HTTP method. Defaults to GET.",
                },
                "body": {
                    "type": "string",
                    "description": "Request body as a string (for POST/PUT).",
                },
            },
            "required": ["url"],
        },
        requires_confirmation=True,  # Outbound requests always require user approval
        handler=_http_request_handler,
    ),
    "calendar_lookup": ToolDefinition(
        name="calendar_lookup",
        description=(
            "Look up calendar events for a specific date. "
            "Returns availability and scheduled events from Google Calendar."
        ),
        parameters={
            "type": "object",
            "properties": {
                "date": {
                    "type": "string",
                    "description": "The date to check in YYYY-MM-DD format.",
                },
                "calendar_id": {
                    "type": "string",
                    "description": "Google Calendar ID. Defaults to 'primary'.",
                },
            },
            "required": ["date"],
        },
        requires_confirmation=False,  # Read-only calendar lookup
        handler=_calendar_lookup_handler,
    ),
}


def get_tools_for_agent(agent: Any) -> dict[str, ToolDefinition]:
    """
    Filter BUILTIN_TOOLS to only those assigned to the given agent.

    Args:
        agent: An Agent ORM instance with a tool_assignments list attribute.

    Returns:
        Dict mapping tool name → ToolDefinition for each tool in
        agent.tool_assignments that exists in BUILTIN_TOOLS. Tools that are
        assigned but not in BUILTIN_TOOLS are silently ignored.
    """
    assigned: list[str] = agent.tool_assignments or []
    return {name: tool for name, tool in BUILTIN_TOOLS.items() if name in assigned}


def to_litellm_format(tools: dict[str, ToolDefinition]) -> list[dict[str, Any]]:
    """
    Convert a tool registry dict to OpenAI function-calling schema.

    This is the format expected by LiteLLM's `tools` parameter in
    acompletion() calls, which follows the OpenAI function-calling spec:

        [
            {
                "type": "function",
                "function": {
                    "name": "...",
                    "description": "...",
                    "parameters": { ... JSON Schema ... }
                }
            },
            ...
        ]

    Args:
        tools: Dict of tool name → ToolDefinition.

    Returns:
        List of OpenAI-format tool dicts ready to pass to LiteLLM.
    """
    return [
        {
            "type": "function",
            "function": {
                "name": tool.name,
                "description": tool.description,
                "parameters": tool.parameters,
            },
        }
        for tool in tools.values()
    ]
