import logging
# import re
import json
import functools
from typing import Optional, Tuple
logger = logging.getLogger('sigmund')


class BaseTool:
    """A base class for tools that process an AI reply."""
    
    def __init__(self, sigmund):
        self._sigmund = sigmund
        
    @property
    def tool_spec(self):
        """The tool spec should corresond to the OpenAI specification for
        function tools.
        """
        return {
            "name": self.__class__.__name__,
            "description": self.__doc__,
            "parameters": {
                "type": "object",
                "properties": self.arguments,
                "required": self.required_arguments,
            }
        }
        
    @property
    def name(self):
        return self.__class__.__name__
    
    def bind(self, args: str, message_prefix: str | None = None) -> callable:
        """Returns a callable that corresponds to a tool function called with
        a string of arguments, which should be in JSON format. The callable 
        itself returns a (progress_message: str, result_message, result: dict,
        needs_feedback: bool) tuple, where messages are informative text message 
        as generated by the tool, result is a dict with name, args, and content 
        keys that correspond to the name and arguments of the function and the 
        result of the tool call. needs_feedback indicates whether the model 
        should be called again to provide feedback based on the tool result.
        """
        def bound_tool_function():
            try:
                kwargs = json.loads(args)
                # import pprint
                # print('*** arguments')
                # pprint.pprint(kwargs)
                # print('***')
                tool_response = self(**kwargs)
            except Exception as e:
                message = 'Failed to run tool'
                result = f'The following error occurred while trying to run tool:\n\n{e}'
                needs_feedback = True
                language = 'markdown'
            else:
                if len(tool_response) == 3:
                    message, result, needs_feedback = tool_response
                    language = 'markdown'
                elif len(tool_response) == 4:
                    message, result, language, needs_feedback = tool_response
                else:
                    raise ValueError(f'Invalid tool response: {tool_response}')
            result = {'name': self.name,
                      'args': args,
                      'content': result}
            if message_prefix is not None:
                message = message_prefix + message
            return message, result, language, needs_feedback
        return bound_tool_function
        
    def __call__(self) -> Tuple[str, Optional[str], bool]:
        """Should be implemented in a tool with additional arguments that
        match tool specification.
        """
        raise NotImplementedError()
