"""
Tool Registry - Central tool management for MCP

Manages tool definitions with type safety and validation.
Part of Option B clean architecture refactoring.
"""

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

from pydantic import BaseModel, ConfigDict, Field

logger = logging.getLogger(__name__)


class ToolDefinition(BaseModel):
    """
    Type-safe tool definition.

    Represents a single MCP tool with all necessary metadata.

    Attributes:
        name: Unique tool identifier (e.g., "wordpress_get_post")
        description: Human-readable tool description
        input_schema: JSON Schema for tool parameters
        handler: Async function that executes the tool
        required_scope: Required API key scope ("read", "write", "admin")
        plugin_type: Plugin type this tool belongs to (e.g., "wordpress")
        category: Tool category for scope-based visibility filtering (F.7)
            One of: "read", "read_sensitive", "lifecycle", "crud", "env",
            "backup", "system". Defaults to "read" for backward compatibility.
        sensitivity: "normal" or "sensitive" (logs, envs, backups, connection strings).
    """

    name: str = Field(..., description="Unique tool identifier")
    description: str = Field(..., description="Tool description")
    input_schema: dict[str, Any] = Field(
        default_factory=lambda: {"type": "object", "properties": {}},
        description="JSON Schema for parameters",
    )
    handler: Callable = Field(..., description="Async handler function")
    required_scope: str = Field(
        default="read", description="Required API key scope (read/write/admin)"
    )
    plugin_type: str = Field(..., description="Plugin type (wordpress, gitea, etc)")
    category: str = Field(
        default="read",
        description="Tool category for scope-based visibility (F.7)",
    )
    sensitivity: str = Field(
        default="normal",
        description="Data sensitivity: normal or sensitive (F.7)",
    )

    model_config = ConfigDict(arbitrary_types_allowed=True)  # Allow Callable type


class ToolRegistry:
    """
    Central registry for all MCP tools.

    Manages tool registration, retrieval, and validation.
    Ensures unique tool names and provides filtering by plugin type.

    Examples:
        >>> registry = ToolRegistry()
        >>> tool = ToolDefinition(
        ...     name="wordpress_list_posts",
        ...     description="List WordPress posts",
        ...     handler=async_handler_func,
        ...     plugin_type="wordpress"
        ... )
        >>> registry.register(tool)
        >>> all_tools = registry.get_all()
        >>> wp_tools = registry.get_by_plugin_type("wordpress")
    """

    def __init__(self):
        """Initialize empty tool registry."""
        self.tools: dict[str, ToolDefinition] = {}
        self.logger = logging.getLogger("ToolRegistry")
        self.logger.info("ToolRegistry initialized")

    def register(self, tool: ToolDefinition) -> None:
        """
        Register a tool in the registry.

        Args:
            tool: Tool definition to register

        Raises:
            ValueError: If tool name already exists

        Examples:
            >>> registry.register(tool_definition)
        """
        if tool.name in self.tools:
            raise ValueError(f"Tool '{tool.name}' already registered")

        self.tools[tool.name] = tool
        self.logger.debug(f"Registered tool: {tool.name} ({tool.plugin_type})")

    def register_many(self, tools: list[ToolDefinition]) -> int:
        """
        Register multiple tools at once.

        Args:
            tools: List of tool definitions

        Returns:
            Number of tools successfully registered

        Examples:
            >>> count = registry.register_many([tool1, tool2, tool3])
            >>> print(f"Registered {count} tools")
        """
        count = 0
        for tool in tools:
            try:
                self.register(tool)
                count += 1
            except ValueError as e:
                self.logger.warning(f"Skipped duplicate tool: {e}")
            except Exception as e:
                self.logger.error(f"Failed to register tool: {e}", exc_info=True)

        self.logger.info(f"Registered {count}/{len(tools)} tools")
        return count

    def get_all(self) -> list[ToolDefinition]:
        """
        Get all registered tools.

        Returns:
            List of all tool definitions

        Examples:
            >>> tools = registry.get_all()
            >>> print(f"Total tools: {len(tools)}")
        """
        return list(self.tools.values())

    def get_by_name(self, name: str) -> ToolDefinition | None:
        """
        Get a tool by its name.

        Args:
            name: Tool name

        Returns:
            Tool definition if found, None otherwise

        Examples:
            >>> tool = registry.get_by_name("wordpress_get_post")
            >>> if tool:
            ...     print(f"Found: {tool.description}")
        """
        return self.tools.get(name)

    def get_by_plugin_type(self, plugin_type: str) -> list[ToolDefinition]:
        """
        Get all tools for a specific plugin type.

        Args:
            plugin_type: Plugin type (e.g., "wordpress", "gitea")

        Returns:
            List of tools for the specified plugin type

        Examples:
            >>> wp_tools = registry.get_by_plugin_type("wordpress")
            >>> print(f"WordPress tools: {len(wp_tools)}")
        """
        return [tool for tool in self.tools.values() if tool.plugin_type == plugin_type]

    def get_count(self) -> int:
        """
        Get total number of registered tools.

        Returns:
            Count of registered tools

        Examples:
            >>> count = registry.get_count()
        """
        return len(self.tools)

    def get_count_by_plugin(self) -> dict[str, int]:
        """
        Get tool counts grouped by plugin type.

        Returns:
            Dictionary mapping plugin type to tool count

        Examples:
            >>> counts = registry.get_count_by_plugin()
            >>> print(counts)  # {'wordpress': 95, 'gitea': 50}
        """
        counts = {}
        for tool in self.tools.values():
            plugin_type = tool.plugin_type
            counts[plugin_type] = counts.get(plugin_type, 0) + 1
        return counts

    def clear(self) -> None:
        """
        Clear all registered tools.

        Primarily for testing purposes.

        Examples:
            >>> registry.clear()
            >>> assert registry.get_count() == 0
        """
        self.tools.clear()
        self.logger.info("Tool registry cleared")

    def __repr__(self) -> str:
        """String representation of registry."""
        counts = self.get_count_by_plugin()
        counts_str = ", ".join(f"{k}: {v}" for k, v in counts.items())
        return f"ToolRegistry(total={self.get_count()}, {counts_str})"


# Singleton instance
_tool_registry: ToolRegistry | None = None


def get_tool_registry() -> ToolRegistry:
    """
    Get the singleton tool registry instance.

    Returns:
        Global ToolRegistry instance

    Examples:
        >>> registry = get_tool_registry()
        >>> registry.register(tool)
    """
    global _tool_registry
    if _tool_registry is None:
        _tool_registry = ToolRegistry()
    return _tool_registry
