# This file was generated by liblab | https://liblab.com/

"""
Creates a Toolhouse class.
Generates the main SDK with all available queries as attributes.

Class:
    Toolhouse
"""
import os
from enum import Enum
from typing import List, Union, Dict, Any, Optional

from .exceptions import ToolhouseError
from .net.environment import Environment
from .services.tools import Tools
from .services.local_tools import LocalTools
from .models.Provider import Provider as ProviderModel
from .models.RunToolsRequest import RunToolsRequest
from .models.GetToolsRequest import GetToolsRequest
try:
    from .models.OpenAIStream import stream_to_chat_completion
except ImportError:
    stream_to_chat_completion = None  # type: ignore


class Toolhouse:
    """
    A class representing the full Toolhouse SDK

    Attributes
    ----------
    tools : Tools

    Methods
    -------
    set_base_url(url: str)
        Sets the end URL
    set_access_token(access_token)
        Set the access token
    """

    def __init__(self, access_token: Optional[str] = None,
                 provider: Union[ProviderModel, str] = ProviderModel.OPENAI,
                 environment: Environment = Environment.DEFAULT
                 ) -> None:
        """
         Initializes the Toolhouse SDK class.

         Parameters
         ----------
         access_token : str
             The access token
         provider : ProviderModel
             The provider model or name
         environment : Environment
             The environment that the SDK is accessing
         """
        if not provider:
            raise ValueError(
                "Parameter provider is required, cannot be empty or blank.")
        self.provider = self._enum_matching(
            provider, ProviderModel.list(), "provider")
        if access_token is None:
            access_token = os.environ.get("TOOLHOUSE_API_KEY", None)
        if access_token is None:
            raise ToolhouseError(
                "The access_token client option must be set either by passing access_token to the SDK or by setting the TOOLHOUSE_API_KEY environment variable"
            )
        self.api_key = access_token
        self.tools = Tools(access_token)
        self.metadata: Dict[str, Any] = {}
        self.set_base_url(environment.value if isinstance(
            environment, Environment) else environment)
        self.local_tools: LocalTools = LocalTools()

    def register_local_tool(self, local_tool):
        """Register Local Tools"""
        return self.local_tools.register_local_tool(local_tool)
    
    def set_metadata(self, key: str, value) -> None:
        """
        Sets User Metadata

        Parameters
        ----------
            key
            value
        """
        self.metadata[key] = value

    def set_provider(self, provider: ProviderModel) -> None:
        """
        Sets User Metadata

        Parameters
        ----------
        provider : ProviderModel
            The provider model or name
        """
        self.provider = self._enum_matching(
            provider, ProviderModel.list(), "provider")

    def set_base_url(self, url: str) -> None:
        """
        Sets the end URL

        Parameters
        ----------
            url:
                The end URL
        """
        self.tools.set_base_url(url)

    def set_access_token(self, token: str) -> None:
        """
        Sets auth token key

        Parameters
        ----------
        token: string
            Auth token value
        """
        self.tools.set_access_token(token)

    def get_tools(self):
        """
        Get Tools
        """
        return self.tools.get_tools(GetToolsRequest(provider=self.provider, metadata=self.metadata))

    def run_tools(self, response, append: bool = True, stream=False) -> List:
        """
        Run Tools based on the response.
        Parameters
        ----------
        response : Any
            The response from the provider
        
        append : bool
            Appends the LLM response to the list of messages in the return value.

        Returns
        -------
        List
            A list of messages
        """
        messages: List = []

        if self.provider in ("openai", ProviderModel.OPENAI):
            if stream:
                if stream_to_chat_completion is None:
                    raise ImportError("OpenAI Package is required for this function.")
                response = stream_to_chat_completion(response)
                if response is None:
                    return []
            if response.choices[0].finish_reason != 'tool_calls':
                return []
            response_message = response.choices[0].message
            if append:
                msg = response_message.model_dump()
                del msg["function_call"]
                messages.append(msg)
            tool_calls = getattr(response_message, 'tool_calls', None)

            if tool_calls:
                for tool in tool_calls:
                    if tool.function.name in self.local_tools.get_registered_tools():
                        result = self.local_tools.run_tools(tool)
                        messages.append(result.model_dump())
                    else:
                        run_tool_request = RunToolsRequest(
                            tool, self.provider, self.metadata)
                        run_response = self.tools.run_tools(run_tool_request)
                        messages.append(run_response.content)
        
        elif self.provider in ("anthropic", ProviderModel.ANTHROPIC):
            if response.stop_reason != 'tool_use':
                return []

            message: dict = {'role': 'user', 'content': []}
            for tool in response.content:
                if tool.type == "tool_use":
                    if stream:
                        tool = tool.model_dump()
                    if tool.name in self.local_tools.get_registered_tools():
                        result = self.local_tools.run_tools(tool)
                        message['content'].append(result.model_dump())
                    else:
                        run_tool_request = RunToolsRequest(
                            tool, self.provider, self.metadata)
                        run_response = self.tools.run_tools(run_tool_request)
                        output = run_response.content
                        message['content'].append(output)
            if message['content']:
                if append:
                    messages.append({'role': 'assistant', 'content': response.content})
                messages.append(message)

        else:
            raise NotImplementedError("Provider not supported")

        return messages

    @classmethod
    def _enum_matching(
        cls, value: Union[str, Enum], enum_values: List[str], variable_name: str
    ):
        str_value = value.value if isinstance(value, Enum) else value
        if str_value in enum_values:
            return value
        else:
            raise ValueError(
                f"Invalid value for {variable_name}: must match one of {enum_values}"
            )
