"""
ToolRegistry - Centralized registry for managing groknroll tools

Following OpenCode architecture, provides:
- Singleton pattern for global tool access
- Tool registration with duplicate name prevention
- Tool lookup by name
- Auto-discovery of tools in groknroll/tools/ directory
- Type-safe implementation
"""

from pathlib import Path
from typing import Optional
import importlib
import inspect

from groknroll.tools.base_tool import BaseTool


class ToolRegistry:
    """
    Centralized registry for managing tools

    Singleton pattern ensures one global registry instance.
    Tools are registered by name and can be retrieved for use.

    Features:
    - Auto-discovery of tools in groknroll/tools/ directory
    - Duplicate name prevention
    - Fast O(1) lookup by name
    - List all registered tools

    Example:
        registry = ToolRegistry()
        registry.register(ReadTool())

        tool = registry.get("read")
        if tool:
            result = await tool.execute(path="file.txt")
    """

    _instance: Optional["ToolRegistry"] = None

    def __new__(cls) -> "ToolRegistry":
        """Singleton pattern - only one instance exists"""
        if cls._instance is None:
            cls._instance = super().__new__(cls)
            cls._instance._tools: dict[str, BaseTool] = {}
            cls._instance._initialized = False
        return cls._instance

    def __init__(self):
        """Initialize registry (only runs once due to singleton)"""
        if not self._initialized:
            self._tools: dict[str, BaseTool] = {}
            self._initialized = True

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

        Args:
            tool: Tool instance to register (must inherit from BaseTool)

        Raises:
            TypeError: If tool does not inherit from BaseTool
            ValueError: If tool name already registered

        Example:
            registry = ToolRegistry()
            registry.register(ReadTool())
        """
        if not isinstance(tool, BaseTool):
            raise TypeError(
                f"Tool must inherit from BaseTool, got {type(tool).__name__}"
            )

        tool_name = tool.name

        if tool_name in self._tools:
            raise ValueError(
                f"Tool '{tool_name}' is already registered. "
                f"Use a different name or unregister the existing tool first."
            )

        self._tools[tool_name] = tool

    def unregister(self, name: str) -> None:
        """
        Unregister a tool from the registry

        Args:
            name: Name of the tool to unregister

        Raises:
            KeyError: If tool name not found in registry

        Example:
            registry = ToolRegistry()
            registry.unregister("read")
        """
        if name not in self._tools:
            raise KeyError(f"Tool '{name}' not found in registry")

        del self._tools[name]

    def get(self, name: str) -> Optional[BaseTool]:
        """
        Get a tool by name

        Args:
            name: Name of the tool to retrieve

        Returns:
            Tool instance if found, None otherwise

        Example:
            registry = ToolRegistry()
            tool = registry.get("read")
            if tool:
                result = await tool.execute(path="file.txt")
        """
        return self._tools.get(name)

    def list_tools(self) -> list[BaseTool]:
        """
        List all registered tools

        Returns:
            List of all tool instances in the registry

        Example:
            registry = ToolRegistry()
            for tool in registry.list_tools():
                print(f"{tool.name}: {tool.description}")
        """
        return list(self._tools.values())

    def has_tool(self, name: str) -> bool:
        """
        Check if a tool is registered

        Args:
            name: Name of the tool to check

        Returns:
            True if tool is registered, False otherwise

        Example:
            registry = ToolRegistry()
            if registry.has_tool("read"):
                tool = registry.get("read")
        """
        return name in self._tools

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

        Useful for testing or resetting the registry.

        Example:
            registry = ToolRegistry()
            registry.clear()  # Remove all tools
        """
        self._tools.clear()

    def auto_discover(self, tools_dir: Optional[Path] = None) -> int:
        """
        Auto-discover and register all tools in groknroll/tools/ directory

        Scans for all *_tool.py files (except base_tool.py and tool_registry.py),
        imports them, finds BaseTool subclasses, and registers instances.

        Args:
            tools_dir: Directory to scan for tools (defaults to groknroll/tools/)

        Returns:
            Number of tools discovered and registered

        Raises:
            ImportError: If a tool module cannot be imported

        Example:
            registry = ToolRegistry()
            count = registry.auto_discover()
            print(f"Discovered {count} tools")
        """
        if tools_dir is None:
            # Get the directory containing this file (groknroll/tools/)
            tools_dir = Path(__file__).parent

        discovered_count = 0

        # Find all *_tool.py files except base_tool.py and tool_registry.py
        for tool_file in tools_dir.glob("*_tool.py"):
            if tool_file.name in ("base_tool.py", "tool_registry.py"):
                continue

            # Import the module
            module_name = f"groknroll.tools.{tool_file.stem}"
            try:
                module = importlib.import_module(module_name)
            except ImportError as e:
                raise ImportError(f"Failed to import {module_name}: {e}")

            # Find all BaseTool subclasses in the module
            for name, obj in inspect.getmembers(module, inspect.isclass):
                # Skip BaseTool itself and non-subclasses
                if obj is BaseTool or not issubclass(obj, BaseTool):
                    continue

                # Skip abstract classes (those with abstract methods)
                if inspect.isabstract(obj):
                    continue

                # Create instance and register
                try:
                    tool_instance = obj()

                    # Only register if not already registered
                    if not self.has_tool(tool_instance.name):
                        self.register(tool_instance)
                        discovered_count += 1
                except Exception:
                    # Skip tools that can't be instantiated
                    continue

        return discovered_count

    def __len__(self) -> int:
        """Return number of registered tools"""
        return len(self._tools)

    def __contains__(self, name: str) -> bool:
        """Check if tool name is registered (supports 'in' operator)"""
        return name in self._tools

    def __str__(self) -> str:
        """String representation"""
        return f"ToolRegistry({len(self._tools)} tools registered)"

    def __repr__(self) -> str:
        """Detailed string representation"""
        tool_names = sorted(self._tools.keys())
        return f"ToolRegistry(tools={tool_names})"
