"""Agent tools — capabilities that agents can invoke during processing."""

from __future__ import annotations

import logging
from collections.abc import Awaitable, Callable
from typing import Any

from pydantic import BaseModel, Field

logger = logging.getLogger(__name__)


class ToolDefinition(BaseModel):
    """Declarative definition of an agent tool."""

    name: str
    description: str
    parameters: dict[str, Any] = Field(default_factory=dict)
    required_permissions: list[str] = Field(default_factory=list)


class AgentTool:
    """
    A tool that an agent can invoke.

    Wraps an async callable with metadata for the LLM function-calling interface.
    """

    def __init__(
        self,
        definition: ToolDefinition,
        handler: Callable[..., Awaitable[Any]],
    ) -> None:
        self.definition = definition
        self._handler = handler

    @property
    def name(self) -> str:
        return self.definition.name

    async def execute(self, **kwargs: Any) -> Any:
        """Execute the tool with the given arguments."""
        logger.info("Executing tool: %s with args: %s", self.name, list(kwargs.keys()))
        try:
            return await self._handler(**kwargs)
        except Exception:
            logger.exception("Tool %s failed", self.name)
            raise

    def to_function_schema(self) -> dict[str, Any]:
        """Export as an OpenAI-compatible function schema."""
        return {
            "type": "function",
            "function": {
                "name": self.definition.name,
                "description": self.definition.description,
                "parameters": self.definition.parameters,
            },
        }


class ToolRegistry:
    """Central registry of all available tools."""

    def __init__(self) -> None:
        self._tools: dict[str, AgentTool] = {}

    def register(self, tool: AgentTool) -> None:
        """Register a tool."""
        self._tools[tool.name] = tool
        logger.info("Tool registered: %s", tool.name)

    def get(self, name: str) -> AgentTool | None:
        """Get a tool by name."""
        return self._tools.get(name)

    def get_tools_for_agent(self, tool_names: list[str]) -> list[AgentTool]:
        """Return the subset of tools that an agent is allowed to use."""
        return [self._tools[n] for n in tool_names if n in self._tools]

    def list_tools(self) -> list[str]:
        """List all registered tool names."""
        return list(self._tools.keys())


# ── Built-in Tools ───────────────────────────────────────


def create_builtin_tools(knowledge_store: Any = None) -> list[AgentTool]:
    """Create the set of built-in tools available to agents."""
    tools: list[AgentTool] = []

    # ── Knowledge Search ─────────────────────────────
    async def search_knowledge(query: str, top_k: int = 5) -> list[dict]:
        if knowledge_store is None:
            return [{"error": "Knowledge store not available"}]
        return await knowledge_store.search(query=query, top_k=top_k)

    tools.append(
        AgentTool(
            definition=ToolDefinition(
                name="search_knowledge",
                description="Search the Vastian Knowledge Bank for relevant information.",
                parameters={
                    "type": "object",
                    "properties": {
                        "query": {"type": "string", "description": "The search query"},
                        "top_k": {
                            "type": "integer",
                            "description": "Number of results",
                            "default": 5,
                        },
                    },
                    "required": ["query"],
                },
            ),
            handler=search_knowledge,
        )
    )

    # ── Read Document ────────────────────────────────
    async def read_document(doc_path: str) -> str:
        if knowledge_store is None:
            return "Knowledge store not available"
        return await knowledge_store.read_document(doc_path)

    tools.append(
        AgentTool(
            definition=ToolDefinition(
                name="read_document",
                description="Read one of the Vastian Network's managed documents.",
                parameters={
                    "type": "object",
                    "properties": {
                        "doc_path": {"type": "string", "description": "Path to the document"},
                    },
                    "required": ["doc_path"],
                },
            ),
            handler=read_document,
        )
    )

    # ── Write Document ───────────────────────────────
    async def write_document(doc_path: str, content: str, author: str) -> str:
        if knowledge_store is None:
            return "Knowledge store not available"
        await knowledge_store.write_document(doc_path, content, author)
        return f"Document written: {doc_path}"

    tools.append(
        AgentTool(
            definition=ToolDefinition(
                name="write_document",
                description="Create or update a managed document. Only use for documents you own.",
                parameters={
                    "type": "object",
                    "properties": {
                        "doc_path": {"type": "string", "description": "Document path"},
                        "content": {"type": "string", "description": "Document content (Markdown)"},
                        "author": {"type": "string", "description": "Your agent ID"},
                    },
                    "required": ["doc_path", "content", "author"],
                },
                required_permissions=["document_owner"],
            ),
            handler=write_document,
        )
    )

    # ── Store Insight ────────────────────────────────
    async def store_insight(content: str, tags: list[str], author: str) -> str:
        if knowledge_store is None:
            return "Knowledge store not available"
        entry_id = await knowledge_store.store_entry(content=content, author=author, tags=tags)
        return f"Insight stored: {entry_id}"

    tools.append(
        AgentTool(
            definition=ToolDefinition(
                name="store_insight",
                description="Store a new insight or piece of knowledge in the Vastian Knowledge Bank.",
                parameters={
                    "type": "object",
                    "properties": {
                        "content": {"type": "string", "description": "The insight to store"},
                        "tags": {
                            "type": "array",
                            "items": {"type": "string"},
                            "description": "Tags for categorization",
                        },
                        "author": {"type": "string", "description": "Your agent ID"},
                    },
                    "required": ["content", "tags", "author"],
                },
            ),
            handler=store_insight,
        )
    )

    return tools
