from enum import Enum
from typing import Any, Optional
from common.log import logger
import copy


class ToolStage(Enum):
    """Enum representing tool decision stages"""
    PRE_PROCESS = "pre_process"  # Tools that need to be actively selected by the agent
    POST_PROCESS = "post_process"  # Tools that automatically execute after final_answer


class ToolResult:
    """Tool execution result"""
    
    def __init__(self, status: str = None, result: Any = None, ext_data: Any = None):
        self.status = status
        self.result = result
        self.ext_data = ext_data

    @staticmethod
    def success(result, ext_data: Any = None):
        return ToolResult(status="success", result=result, ext_data=ext_data)

    @staticmethod
    def fail(result, ext_data: Any = None):
        return ToolResult(status="error", result=result, ext_data=ext_data)


class BaseTool:
    """Base class for all tools."""

    # Default decision stage is pre-process
    stage = ToolStage.PRE_PROCESS

    # Class attributes must be inherited
    name: str = "base_tool"
    description: str = "Base tool"
    params: dict = {}  # Store JSON Schema
    model: Optional[Any] = None  # LLM model instance, type depends on bot implementation
    progress_callback = None
    cancel_event = None
    # Workspace directory, injected per run. Declared here so a tool that
    # resolves relative paths cannot silently miss the injection.
    cwd: Optional[str] = None

    def is_cancelled(self) -> bool:
        """True once the user asked to stop the run.

        Long-running tools should poll this and abort early; the agent loop
        checkpoint right after the tool returns turns it into a clean cancel.
        """
        event = getattr(self, "cancel_event", None)
        return event is not None and event.is_set()

    def report_progress(self, message: str):
        callback = getattr(self, "progress_callback", None)
        if not callback:
            return
        try:
            callback(str(message))
        except Exception as e:
            logger.debug(f"[{self.name}] progress callback failed: {e}")

    @classmethod
    def get_json_schema(cls) -> dict:
        """Get the standard description of the tool"""
        return {
            "name": cls.name,
            "description": cls.description,
            "parameters": cls.params
        }

    def execute_tool(self, params: dict) -> ToolResult:
        try:
            return self.execute(params)
        except Exception as e:
            logger.error(e)

    def execute(self, params: dict) -> ToolResult:
        """Specific logic to be implemented by subclasses"""
        raise NotImplementedError

    @classmethod
    def _parse_schema(cls) -> dict:
        """Convert JSON Schema to Pydantic fields"""
        fields = {}
        for name, prop in cls.params["properties"].items():
            # Convert JSON Schema types to Python types
            type_map = {
                "string": str,
                "number": float,
                "integer": int,
                "boolean": bool,
                "array": list,
                "object": dict
            }
            fields[name] = (
                type_map[prop["type"]],
                prop.get("default", ...)
            )
        return fields

    def should_auto_execute(self, context) -> bool:
        """
        Determine if this tool should be automatically executed based on context.

        :param context: The agent context
        :return: True if the tool should be executed, False otherwise
        """
        # Only tools in post-process stage will be automatically executed
        return self.stage == ToolStage.POST_PROCESS

    def close(self):
        """
        Close any resources used by the tool.
        This method should be overridden by tools that need to clean up resources
        such as browser connections, file handles, etc.

        By default, this method does nothing.
        """
        pass
