"""
Base class for all tools.

Each tool is self-describing: name, description, and parameters (JSON Schema).
The schema() method builds the LLM-facing function spec automatically.

current_session_id — ContextVar set by Agent before every tool call.
Tools that need per-session state (e.g. SSH connection pool) read it here.
"""

import asyncio
from abc import ABC, abstractmethod
from contextvars import ContextVar
from dataclasses import dataclass, field

from navi.llm.base import ToolSchema

# Set by Agent before every tool call. Tools that need per-session state read this.
current_session_id: ContextVar[str | None] = ContextVar("current_session_id", default=None)

# Set by run_stream() before executing a tool. run_ephemeral() reads this to forward
# sub-agent tool events up to the parent WS stream.
current_event_sink: ContextVar[asyncio.Queue | None] = ContextVar("current_event_sink", default=None)


@dataclass
class ToolResult:
    success: bool
    output: str  # always a string — LLM consumes this
    error: str | None = None
    metadata: dict = field(default_factory=dict)

    def to_message_content(self) -> str:
        if self.success:
            return self.output
        return f"Error: {self.error}"


class Tool(ABC):
    """Abstract base for all tools."""

    # Override in subclasses:
    name: str
    description: str
    parameters: dict  # JSON Schema object

    @abstractmethod
    async def execute(self, params: dict) -> ToolResult:
        """Execute the tool with given parameters."""

    def schema(self) -> ToolSchema:
        return ToolSchema(
            type="function",
            function={
                "name": self.name,
                "description": self.description,
                "parameters": self.parameters,
            },
        )
