# File: autobyteus/autobyteus/tools/base_tool.py

import logging
from abc import ABC, abstractmethod
from typing import Optional, Any, TYPE_CHECKING, List as TypingList, Dict
import xml.sax.saxutils

from autobyteus.events.event_emitter import EventEmitter
from autobyteus.events.event_types import EventType

from .tool_meta import ToolMeta
from .tool_state import ToolState

if TYPE_CHECKING:
    from autobyteus.agent.context import AgentContext
    from autobyteus.tools.parameter_schema import ParameterSchema
    from autobyteus.tools.tool_config import ToolConfig
    from .tool_state import ToolState
    from autobyteus.tools.registry import ToolDefinition

logger = logging.getLogger('autobyteus')

class BaseTool(ABC, EventEmitter, metaclass=ToolMeta):
    """
    Abstract base class for all tools, with auto-registration via ToolMeta.
    """
    def __init__(self, config: Optional['ToolConfig'] = None):
        super().__init__()
        self.agent_id: Optional[str] = None
        self.definition: Optional['ToolDefinition'] = None # Link back to its definition
        # The config is stored primarily for potential use by subclasses or future base features.
        self._config = config
        # Add a dedicated state dictionary for the tool instance
        # CHANGED: Use ToolState class for explicit state management.
        self.tool_state: 'ToolState' = ToolState()
        logger.debug(f"BaseTool instance initializing for potential class {self.__class__.__name__}. tool_state initialized.")

    @classmethod
    def get_name(cls) -> str:
        """Returns the registered name of the tool."""
        return cls.__name__
    
    @classmethod
    @abstractmethod
    def get_description(cls) -> str:
        """Returns the description of the tool."""
        raise NotImplementedError("Subclasses must implement get_description().")

    @classmethod
    @abstractmethod
    def get_argument_schema(cls) -> Optional['ParameterSchema']: 
        """
        Return a ParameterSchema defining the arguments this tool accepts for execution.
        Return None if the tool accepts no arguments.
        """
        raise NotImplementedError("Subclasses must implement get_argument_schema().")

    @classmethod
    def get_config_schema(cls) -> Optional['ParameterSchema']: 
        """
        Return the ParameterSchema for tool *instantiation* parameters.
        This is optional. By default, tools have no instantiation config.
        """
        return None

    def set_agent_id(self, agent_id: str):
        if not isinstance(agent_id, str) or not agent_id:
            logger.error(f"Attempted to set invalid agent_id: {agent_id} for tool {self.get_name()}")
            return
        self.agent_id = agent_id
        logger.debug(f"Agent ID '{agent_id}' set for tool instance '{self.__class__.get_name()}'")

    async def execute(self, context: 'AgentContext', **kwargs):
        # In this context, self.get_name() will call the instance-specific method if it exists.
        tool_name = self.get_name()
        if self.agent_id is None:
            self.set_agent_id(context.agent_id)
        elif self.agent_id != context.agent_id:
            logger.warning(
                f"Tool '{tool_name}' current agent_id '{self.agent_id}' differs from "
                f"calling context's agent_id '{context.agent_id}'. Updating tool's agent_id."
            )
            self.set_agent_id(context.agent_id)
        
        arg_schema = self.get_argument_schema() 
        if arg_schema:
            is_valid, errors = arg_schema.validate_config(kwargs)
            if not is_valid:
                error_message = f"Invalid arguments for tool '{tool_name}': {'; '.join(errors)}"
                logger.error(error_message)
                raise ValueError(error_message)
        elif kwargs: 
            logger.warning(f"Tool '{tool_name}' does not define an argument schema but received arguments: {kwargs}. These will be passed to _execute.")

        logger.info(f"Executing tool '{tool_name}' for agent '{self.agent_id}' with args: {kwargs}")
        try:
            result = await self._execute(context=context, **kwargs) 
            logger.info(f"Tool '{tool_name}' execution completed successfully for agent '{self.agent_id}'.")
            return result
        except Exception as e:
            logger.error(f"Tool '{tool_name}' execution failed for agent '{self.agent_id}': {type(e).__name__} - {str(e)}", exc_info=True)
            raise

    @abstractmethod
    async def _execute(self, context: 'AgentContext', **kwargs) -> Any:
        raise NotImplementedError("Subclasses must implement the '_execute' method.")

    @classmethod
    def tool_usage(cls) -> str:
        logger.warning("BaseTool.tool_usage() is deprecated. Tool usage is now generated by formatters.")
        # To maintain some backward compatibility without errors, we can generate a basic XML representation.
        # This should ideally not be called by new code.
        from autobyteus.tools.usage.formatters.default_xml_schema_formatter import DefaultXmlSchemaFormatter
        return DefaultXmlSchemaFormatter().provide(cls)
