"""
Tool integration module for CogniCore agents.

This module allows agents to be equipped with tools that they can use to
interact with the outside world, such as performing calculations, searching the
web, or calling external APIs.

The design is centered around the `Tool` abstract base class, which mandates
that every tool must have a `name` and a `description`. This metadata is
critical, as it is provided to the Large Language Model (LLM) to enable it to
make informed decisions about which tool to use and what input to provide.
"""

import abc
import operator
from typing import Dict


class Tool(abc.ABC):
    """
    Abstract Base Class for all tools that an agent can use.

    This class defines the standard interface for all tools. The `name` and
    `description` properties are essential for the agent's reasoning process,
    while the `run` method contains the actual executable logic of the tool.
    """

    @property
    @abc.abstractmethod
    def name(self) -> str:
        """
        A unique, machine-readable name for the tool.

        This name is used by the LLM to specify which tool it wants to call.
        It should consist of letters, numbers, and underscores only.
        Example: "web_search", "calculator".
        """
        raise NotImplementedError

    @property
    @abc.abstractmethod
    def description(self) -> str:
        """
        A detailed, natural-language description of what the tool does.

        This description is provided to the LLM in the prompt, so it must be
        clear and descriptive enough for the model to understand the tool's
        purpose, its capabilities, and the expected format of its input.
        """
        raise NotImplementedError

    @abc.abstractmethod
    def run(self, tool_input: str) -> str:
        """
        Executes the tool with the given input.

        Args:
            tool_input (str): The input for the tool, typically generated by
                              the LLM. The tool's implementation is responsible
                              for parsing this input.

        Returns:
            str: The output of the tool's execution, which will be returned
                 to the agent to continue its reasoning process.
        """
        raise NotImplementedError

    def __repr__(self) -> str:
        """Provides a developer-friendly representation of the tool."""
        return f'<Tool name="{self.name}">'


class CalculatorTool(Tool):
    """
    A tool that allows the agent to perform basic arithmetic calculations.
    """

    @property
    def name(self) -> str:
        """Returns the unique name of the tool."""
        return "calculator"

    @property
    def description(self) -> str:
        """
        Returns the description, which explains how the LLM should use the tool.
        """
        return (
            "A calculator tool for basic arithmetic. "
            "Input should be a simple mathematical expression, "
            "like '2 + 2' or '10 * 4'."
        )

    def __init__(self):
        """Initializes the calculator with supported operations."""
        self._ops: Dict[str, callable] = {
            "+": operator.add,
            "-": operator.sub,
            "*": operator.mul,
            "/": operator.truediv,
        }

    def run(self, tool_input: str) -> str:
        """
        Parses and executes the mathematical expression.

        Args:
            tool_input (str): The mathematical expression to evaluate.

        Returns:
            str: The result of the calculation or an error message if the
                 input is invalid.
        """
        try:
            # Split the input string into its components,
            # e.g., "5 * 3" -> ["5", "*", "3"]
            x_str, op_symbol, y_str = tool_input.strip().split()
            x = float(x_str)
            y = float(y_str)

            if op_symbol not in self._ops:
                return f"""Error: Invalid operator '{op_symbol}'.
                        Use one of {list(self._ops.keys())}"""

            result = self._ops[op_symbol](x, y)
            return str(result)

        except ValueError:
            return (
                "Error: Invalid input format. Please provide an expression "
                "like 'number operator number' (e.g., '5 * 3')."
            )
        except Exception as e:
            return f"An unexpected error occurred: {e}"
