"""
Tool System Foundation

Core abstractions for tool execution:
- Tool: Abstract base class for all tools
- ToolResult: Standardized tool output container
- ToolRegistry: Tool registration and lookup
- ToolExecutor: Tool dispatch and execution
- create_tool_registry: Factory to build fully-loaded registry
"""

from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from enum import Enum
from pathlib import Path
from typing import Any, Optional


class ToolResultStatus(Enum):
    """Status of a tool execution."""
    SUCCESS = "success"
    ERROR = "error"
    PENDING = "pending"


@dataclass
class ToolResult:
    """
    Standardized result from tool execution.
    
    Every tool returns a ToolResult containing:
    - status: success/error/pending
    - output: human-readable output text
    - error: error message if failed
    - data: structured data for programmatic use
    """
    
    status: ToolResultStatus = ToolResultStatus.SUCCESS
    output: str = ""
    error: Optional[str] = None
    data: dict[str, Any] = field(default_factory=dict)

    @property
    def is_success(self) -> bool:
        return self.status == ToolResultStatus.SUCCESS

    @property
    def is_error(self) -> bool:
        return self.status == ToolResultStatus.ERROR

    def to_message(self) -> str:
        """Convert to a message string for conversation history."""
        if self.is_error:
            return f"Error: {self.error}"
        return self.output or "(no output)"

    @classmethod
    def success(cls, output: str = "", data: dict = None) -> "ToolResult":
        """Create a success result."""
        return cls(
            status=ToolResultStatus.SUCCESS,
            output=output,
            data=data or {},
        )

    @classmethod
    def error(cls, error: str) -> "ToolResult":
        """Create an error result."""
        return cls(
            status=ToolResultStatus.ERROR,
            error=error,
        )


class Tool(ABC):
    """
    Abstract base class for all tools.
    
    Every tool must define:
    - name: unique identifier
    - description: what the tool does
    - execute(**kwargs): perform the action and return ToolResult
    
    Tools receive a project_root for path resolution.
    """
    
    def __init__(self, project_root: Optional[Path] = None):
        self._project_root = project_root

    @property
    def project_root(self) -> Optional[Path]:
        return self._project_root

    @project_root.setter
    def project_root(self, value: Path):
        self._project_root = value

    @property
    @abstractmethod
    def name(self) -> str:
        """Unique name for this tool (e.g., 'read_file')."""
        ...

    @property
    @abstractmethod
    def description(self) -> str:
        """Human-readable description of what the tool does."""
        ...

    @abstractmethod
    def execute(self, **kwargs) -> ToolResult:
        """Execute the tool with given arguments. Must return ToolResult."""
        ...

    def resolve_path(self, path: str) -> Path:
        """Resolve a relative path against the project root."""
        if self._project_root is None:
            return Path(path).resolve()
        
        p = Path(path)
        if p.is_absolute():
            return p.resolve()
        return (self._project_root / path).resolve()

    def validate_path(self, path: Path) -> tuple[bool, Optional[str]]:
        """Validate that a path is within the project root."""
        if self._project_root is None:
            return True, None
        
        try:
            path.resolve().relative_to(self._project_root.resolve())
            return True, None
        except ValueError:
            return False, f"Path '{path}' is outside the project root"


class ToolRegistry:
    """
    Registry for available tools.
    
    Manages tool registration and lookup by name.
    Tools are registered once and retrieved by the agent loop.
    """
    
    def __init__(self):
        self._tools: dict[str, Tool] = {}

    def register(self, tool: Tool) -> None:
        """Register a tool by its name."""
        self._tools[tool.name] = tool

    def get(self, name: str) -> Optional[Tool]:
        """Get a tool by name, or None if not found."""
        return self._tools.get(name)

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

    def get_all(self) -> dict[str, Tool]:
        """Get all registered tools."""
        return dict(self._tools)

    def __contains__(self, name: str) -> bool:
        return name in self._tools

    def __len__(self) -> int:
        return len(self._tools)


class ToolExecutor:
    """
    Executes tools from a registry by name.
    
    Provides a single dispatch point for executing any registered tool.
    Handles argument passing and error wrapping.
    """
    
    def __init__(self, registry: ToolRegistry):
        self.registry = registry

    def execute(self, tool_name: str, arguments: dict) -> ToolResult:
        """Execute a named tool with the given arguments."""
        tool = self.registry.get(tool_name)
        
        if tool is None:
            return ToolResult.error(f"Unknown tool: '{tool_name}'")
        
        try:
            return tool.execute(**arguments)
        except TypeError as e:
            return ToolResult.error(f"Invalid arguments for '{tool_name}': {e}")
        except Exception as e:
            return ToolResult.error(f"Tool '{tool_name}' failed: {e}")


def create_tool_registry(project_root: Path) -> ToolRegistry:
    """
    Factory to create a fully-loaded tool registry with all built-in tools.
    
    Registers:
    - read_file, write_file, patch_file, delete_file
    - list_files, search_files
    - run_command
    - web_search, read_url
    
    Args:
        project_root: Root directory for path resolution
        
    Returns:
        Populated ToolRegistry
    """
    registry = ToolRegistry()
    
    # File tools
    from ai_coder.tools.file_editor import (
        ReadFileTool,
        WriteFileTool,
        PatchFileTool,
        DeleteFileTool,
        ListFilesTool,
        SearchFilesTool,
    )
    
    for tool_cls in [ReadFileTool, WriteFileTool, PatchFileTool, 
                     DeleteFileTool, ListFilesTool, SearchFilesTool]:
        tool = tool_cls(project_root)
        registry.register(tool)
    
    # Terminal tool
    from ai_coder.tools.terminal import RunCommandTool
    cmd_tool = RunCommandTool(project_root)
    registry.register(cmd_tool)
    
    # Web tools (these don't need project_root but we pass it for consistency)
    try:
        from ai_coder.tools.web_search import WebSearchTool
        registry.register(WebSearchTool(project_root))
    except ImportError:
        pass  # duckduckgo_search not installed
    
    try:
        from ai_coder.tools.url_reader import URLReaderTool
        registry.register(URLReaderTool(project_root))
    except ImportError:
        pass  # httpx/bs4 not installed
    
    return registry
