#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# -------------------------------------------------------------------------
# Copyright (c) 2026 Huawei Technologies Co.,Ltd.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# -------------------------------------------------------------------------
import inspect
from collections.abc import Callable
from dataclasses import dataclass
from typing import Any

from rllm.tools.utils import function_to_dict


@dataclass
class ToolCall:
    name: str
    arguments: dict[str, Any]

    def to_dict(self):
        return {"name": self.name, "arguments": self.arguments}


@dataclass
class ToolOutput:
    name: str
    output: str | list | dict | None = None
    error: str | None = None
    metadata: dict | None = None

    def __str__(self) -> str:
        """Convert the tool output to a string representation."""
        if self.error:
            return f"Error: {self.error}"
        elif self.output is None:
            return ""
        elif isinstance(self.output, list | dict):
            import json

            return json.dumps(self.output)
        else:
            return str(self.output)

    def to_string(self) -> str:
        """
        Convert the tool output to a string representation.

        Example usage:
            tool_output = ToolOutput(name="calculator", output=42)
            result_string = tool_output.to_string()
        """
        return str(self)


class Tool:
    """
    Abstract base class for all tools that provides a common interface.

    All tools should inherit from this class and implement either:
    - forward() for synchronous tools
    - async_forward() for asynchronous tools
    """

    def __init__(self, name: str | None = None, description: str | None = None, function: Callable | None = None):
        """
        Initialize the tool with a name.

        Args:
            name (str): The name of the tool, used for tool registry and calling.
            description (str): A description of the tool's purpose and functionality.
            function (Callable, optional): Function to convert to tool format.
        """
        self.name = name
        self.description = description
        self.function = function

        if function is not None:
            self._json = function_to_dict(function)
            self.name = self._json["function"]["name"]
            self.description = self._json["function"]["description"]
        else:
            if name is None:
                raise ValueError("Name is required for Tool class.")
            if description is None:
                raise ValueError("Description is required for Tool class.")
            # User must provide json
            self._json = self.json
            if self._json is None:
                raise ValueError("Json representation of the tool is required.")

    @property
    def json(self) -> dict[str, Any]:
        """
        Return the tool's information in a standardized format for tool registration.

        Returns:
            Dict[str, Any]: Tool information including name, description, and parameters.
                Expected format:
                {
                    "type": "function",
                    "function": {
                        "name": self.name,
                        "description": "Description of what the tool does",
                        "parameters": {
                            "type": "object",
                            "properties": {
                                # Parameter definitions
                            },
                            "required": ["list", "of", "required", "parameters"]
                        }
                    }
                }
        """
        return self._json

    def forward(self, *args, **kwargs) -> ToolOutput:
        """
        Synchronous implementation of the tool functionality.
        Override this method for synchronous tools.

        Returns:
            Any: The result of the tool execution.
        """
        if self.function is not None:
            try:
                output = self.function(*args, **kwargs)
                return ToolOutput(name=self.name or "unknown", output=output)
            except Exception as e:
                return ToolOutput(name=self.name or "unknown", error=f"{type(e).__name__} - {str(e)}")
        else:
            raise NotImplementedError("Tool must implement forward() method or provide a function")

    async def async_forward(self, *args, **kwargs) -> ToolOutput:
        """
        Asynchronous implementation of the tool functionality.
        Override this method for asynchronous tools.

        Returns:
            Any: The result of the tool execution.
        """
        return self.forward(*args, **kwargs)

    def __call__(self, *args, use_async=False, **kwargs):
        """
        Make the tool instance callable.
        - If use_async is True, delegates to async_forward
        - If use_async is False, delegates to forward
        - If use_async is None (default), uses async_forward if implemented, otherwise forward

        Args:
            *args: Positional arguments to pass to the implementation
            use_async: Whether to use the async implementation (if None, auto-detect)
            **kwargs: Keyword arguments to pass to the implementation

        Returns:
            Any: The result of the tool execution, which may be a coroutine if using async_forward
        """

        has_async = inspect.isfunction(self.__class__.async_forward) or self.__class__.async_forward is Tool.async_forward
        has_sync = not (inspect.isfunction(self.__class__.forward) and self.__class__.forward is Tool.forward)
        # Explicit routing based on use_async flag
        if use_async is True:
            if has_async:
                return self.async_forward(*args, **kwargs)
            else:
                raise NotImplementedError(f"Tool {self.__class__.__name__} does not implement async_forward() but use_async=True was specified.")
        elif use_async is False:
            return self.forward(*args, **kwargs)

        # Auto-detect implementation if use_async is None
        if has_async:
            return self.async_forward(*args, **kwargs)
        elif has_sync:
            return self.forward(*args, **kwargs)
        else:
            raise NotImplementedError(f"Tool {self.__class__.__name__} must implement either forward() or async_forward().")