"""
Tool Executor - Direct tool execution with permission checks

Provides controlled tool execution for the REPL:
- Integration with ToolRegistry for tool lookup
- Integration with PermissionManager for permission checks
- Permission dialogs for tools requiring approval
- Execution result tracking

Examples:
    executor = ToolExecutor(
        registry=ToolRegistry(),
        permission_manager=PermissionManager(project_path),
    )

    # Execute a tool
    result = await executor.execute("read", path="file.txt")
    if result.success:
        print(result.output)
"""

import asyncio
from collections import deque
from dataclasses import dataclass, field
from datetime import datetime
from pathlib import Path
from typing import Any, Callable, Optional

from rich.console import Console
from rich.prompt import Confirm

from groknroll.permissions.manager import PermissionManager
from groknroll.tools.tool_registry import ToolRegistry


@dataclass
class ToolResult:
    """
    Result of a tool execution

    Attributes:
        success: Whether execution succeeded
        output: Output from the tool
        tool_name: Name of the executed tool
        error: Error message if failed
        execution_time: Time taken in seconds
        permission_status: Permission check result
    """

    success: bool
    output: Any
    tool_name: str
    error: Optional[str] = None
    execution_time: float = 0.0
    permission_status: Optional[str] = None

    def to_dict(self) -> dict[str, Any]:
        """Convert to dictionary"""
        return {
            "success": self.success,
            "output": self.output
            if isinstance(self.output, (str, int, float, bool, list, dict))
            else str(self.output),
            "tool_name": self.tool_name,
            "error": self.error,
            "execution_time": self.execution_time,
            "permission_status": self.permission_status,
        }


@dataclass
class ToolCall:
    """
    Record of a tool call

    Attributes:
        tool_name: Name of the tool
        kwargs: Arguments passed to the tool
        result: Result of the execution
        timestamp: When the call was made
    """

    tool_name: str
    kwargs: dict[str, Any]
    result: Optional[ToolResult] = None
    timestamp: datetime = field(default_factory=datetime.now)

    def to_dict(self) -> dict[str, Any]:
        """Convert to dictionary"""
        return {
            "tool_name": self.tool_name,
            "kwargs": self.kwargs,
            "result": self.result.to_dict() if self.result else None,
            "timestamp": self.timestamp.isoformat(),
        }


class ToolExecutor:
    """
    Execute tools with permission checks

    Provides controlled tool execution with integration to the
    permission system and tool registry.

    Example:
        executor = ToolExecutor(project_path=Path.cwd())

        # Execute tool
        result = await executor.execute("read", path="src/main.py")

        # Check history
        for call in executor.get_history():
            print(f"{call.tool_name}: {call.result.success}")
    """

    def __init__(
        self,
        project_path: Optional[Path] = None,
        registry: Optional[ToolRegistry] = None,
        permission_manager: Optional[PermissionManager] = None,
        console: Optional[Console] = None,
        auto_approve: bool = False,
        on_permission_request: Optional[Callable[[str, dict], bool]] = None,
        fsd_mode: bool = False,
    ):
        """
        Initialize tool executor

        Args:
            project_path: Project root path
            registry: Tool registry (creates default if not provided)
            permission_manager: Permission manager (creates default if not provided)
            console: Rich console for permission dialogs
            auto_approve: Auto-approve all permission requests
            on_permission_request: Custom permission request callback
            fsd_mode: FSD Mode - skip ALL permission checks (dangerous!)
        """
        self.project_path = project_path or Path.cwd()
        self.registry = registry or ToolRegistry()
        self.fsd_mode = fsd_mode

        # Create permission manager with FSD mode if not provided
        if permission_manager:
            self.permission_manager = permission_manager
            # Update FSD mode on existing manager
            self.permission_manager.fsd_mode = fsd_mode
        else:
            self.permission_manager = PermissionManager(self.project_path, fsd_mode=fsd_mode)

        self.console = console or Console()
        self.auto_approve = auto_approve or fsd_mode  # FSD mode implies auto-approve
        self.on_permission_request = on_permission_request

        self._history: deque[ToolCall] = deque(maxlen=100)  # O(1) automatic trimming

        # Auto-discover tools if registry is empty
        if len(self.registry) == 0:
            self.registry.auto_discover()

    async def execute(self, tool_name: str, **kwargs) -> ToolResult:
        """
        Execute a tool with permission checks

        Args:
            tool_name: Name of the tool to execute
            **kwargs: Arguments to pass to the tool

        Returns:
            ToolResult with execution details
        """
        import time

        start_time = time.time()

        # Create tool call record
        call = ToolCall(tool_name=tool_name, kwargs=kwargs)

        # Check if tool exists
        tool = self.registry.get(tool_name)
        if tool is None:
            result = ToolResult(
                success=False,
                output=None,
                tool_name=tool_name,
                error=f"Tool '{tool_name}' not found",
            )
            call.result = result
            self._add_to_history(call)
            return result

        # Check permissions
        permission = self.permission_manager.check(tool_name, **kwargs)

        if permission == "deny":
            result = ToolResult(
                success=False,
                output=None,
                tool_name=tool_name,
                error="Permission denied",
                permission_status="deny",
            )
            call.result = result
            self._add_to_history(call)
            return result

        if permission == "ask":
            # Request permission
            granted = await self._request_permission(tool_name, kwargs)
            if not granted:
                result = ToolResult(
                    success=False,
                    output=None,
                    tool_name=tool_name,
                    error="Permission denied by user",
                    permission_status="denied_by_user",
                )
                call.result = result
                self._add_to_history(call)
                return result

        # Execute the tool
        try:
            output = await tool.execute(**kwargs)
            execution_time = time.time() - start_time

            result = ToolResult(
                success=True,
                output=output,
                tool_name=tool_name,
                execution_time=execution_time,
                permission_status="allow",
            )

        except Exception as e:
            execution_time = time.time() - start_time
            result = ToolResult(
                success=False,
                output=None,
                tool_name=tool_name,
                error=str(e),
                execution_time=execution_time,
                permission_status="allow",
            )

        call.result = result
        self._add_to_history(call)
        return result

    def execute_sync(self, tool_name: str, **kwargs) -> ToolResult:
        """
        Synchronous wrapper for execute

        Args:
            tool_name: Name of the tool
            **kwargs: Tool arguments

        Returns:
            ToolResult
        """
        return asyncio.run(self.execute(tool_name, **kwargs))

    async def _request_permission(self, tool_name: str, kwargs: dict) -> bool:
        """
        Request permission from user

        Args:
            tool_name: Tool name
            kwargs: Tool arguments

        Returns:
            True if permission granted
        """
        if self.auto_approve:
            return True

        if self.on_permission_request:
            return self.on_permission_request(tool_name, kwargs)

        # Default: use console prompt
        self.console.print("\n[yellow]Permission Request[/yellow]")
        self.console.print(f"Tool: [cyan]{tool_name}[/cyan]")

        # Show relevant kwargs
        if kwargs:
            for key, value in kwargs.items():
                display_value = str(value)
                if len(display_value) > 50:
                    display_value = display_value[:50] + "..."
                self.console.print(f"  {key}: [dim]{display_value}[/dim]")

        return Confirm.ask("Allow execution?", default=False)

    def _add_to_history(self, call: ToolCall) -> None:
        """Add call to history (deque auto-trims at maxlen)"""
        self._history.append(call)

    def get_history(self, limit: Optional[int] = None) -> list[ToolCall]:
        """
        Get tool call history

        Args:
            limit: Maximum entries to return (newest first)

        Returns:
            List of ToolCall objects
        """
        history = list(reversed(self._history))
        if limit:
            return history[:limit]
        return history

    def clear_history(self) -> None:
        """Clear call history"""
        self._history.clear()

    def list_tools(self) -> list[dict[str, str]]:
        """
        List available tools

        Returns:
            List of tool info dicts
        """
        tools = []
        for tool in self.registry.list_tools():
            tools.append(
                {
                    "name": tool.name,
                    "description": tool.description,
                }
            )
        return tools

    def has_tool(self, name: str) -> bool:
        """Check if tool exists"""
        return self.registry.has_tool(name)

    def get_tool_schema(self, name: str) -> Optional[dict]:
        """
        Get tool's parameter schema

        Args:
            name: Tool name

        Returns:
            Schema dict or None
        """
        tool = self.registry.get(name)
        if tool is None:
            return None

        return {
            "name": tool.name,
            "description": tool.description,
            "parameters": getattr(tool, "parameters", {}),
        }

    def set_agent(self, agent_name: Optional[str]) -> None:
        """
        Set current agent for permission checks

        Args:
            agent_name: Agent name
        """
        self.permission_manager.set_agent(agent_name)

    async def execute_batch(
        self,
        calls: list[tuple[str, dict]],
        stop_on_error: bool = True,
    ) -> list[ToolResult]:
        """
        Execute multiple tools in sequence

        Args:
            calls: List of (tool_name, kwargs) tuples
            stop_on_error: Stop execution on first error

        Returns:
            List of ToolResults
        """
        results = []

        for tool_name, kwargs in calls:
            result = await self.execute(tool_name, **kwargs)
            results.append(result)

            if stop_on_error and not result.success:
                break

        return results


def create_tool_executor(
    project_path: Optional[Path] = None,
    auto_approve: bool = False,
) -> ToolExecutor:
    """
    Create a tool executor (convenience function)

    Args:
        project_path: Project path
        auto_approve: Auto-approve permissions

    Returns:
        Configured ToolExecutor
    """
    return ToolExecutor(project_path=project_path, auto_approve=auto_approve)
