"""
Main.py
"""
from __future__ import annotations

from .chathub import *
from .conversation import *
from .request import *
from .utilities import *


class Chatbot:
    """
    Combines everything to make it seamless
    """

    def __init__(
            self,
            proxy: str | None = None,
            cookies: list[dict] | None = None,
    ) -> None:
        self.proxy: str | None = proxy
        self.chat_hub: ChatHub = ChatHub(
            Conversation(self.proxy, cookies=cookies),
            proxy=self.proxy,
            cookies=cookies,
        )

    @staticmethod
    async def create(
            proxy: str | None = None,
            cookies: list[dict] | None = None,
    ) -> Chatbot:
        self = Chatbot.__new__(Chatbot)
        self.proxy = proxy
        self.chat_hub = ChatHub(
            await Conversation.create(self.proxy, cookies=cookies),
            proxy=self.proxy,
            cookies=cookies,
        )
        return self

    async def save_conversation(self, filename: str) -> None:
        """
        Save the conversation to a file
        """
        with open(filename, "w") as f:
            conversation_id = self.chat_hub.request.conversation_id
            conversation_signature = self.chat_hub.request.conversation_signature
            client_id = self.chat_hub.request.client_id
            invocation_id = self.chat_hub.request.invocation_id
            f.write(
                json.dumps(
                    {
                        "conversation_id": conversation_id,
                        "conversation_signature": conversation_signature,
                        "client_id": client_id,
                        "invocation_id": invocation_id,
                    },
                ),
            )

    async def load_conversation(self, filename: str) -> None:
        """
        Load the conversation from a file
        """
        with open(filename) as f:
            conversation = json.load(f)
            self.chat_hub.request = ChatHubRequest(
                conversation_signature=conversation["conversation_signature"],
                client_id=conversation["client_id"],
                conversation_id=conversation["conversation_id"],
                invocation_id=conversation["invocation_id"],
            )

    async def get_conversation(self) -> dict:
        """
        Gets the conversation history from conversation_id (requires load_conversation)
        """
        return await self.chat_hub.get_conversation()

    async def get_activity(self) -> dict:
        """
        Gets the recent activity (requires cookies)
        """
        return await self.chat_hub.get_activity()

    async def ask(
            self,
            prompt: str,
            wss_link: str = "wss://sydney.bing.com/sydney/ChatHub",
            conversation_style: CONVERSATION_STYLE_TYPE = None,
            webpage_context: str | None = None,
            search_result: bool = False,
            locale: str = guess_locale(),
            simplify_response: bool = False,
    ):
        """
        Ask a question to the bot
        Response:
            {
                item (dict):
                    messages (list[dict]):
                        adaptiveCards (list[dict]):
                            body (list[dict]):
                                text (str): Response
            }
        To get the response, you can do:
            response["item"]["messages"][1]["adaptiveCards"][0]["body"][0]["text"]
        """
        async for final, response in self.chat_hub.ask_stream(
                prompt=prompt,
                conversation_style=conversation_style,
                wss_link=wss_link,
                webpage_context=webpage_context,
                search_result=search_result,
                locale=locale,
        ):
            if final:
                if not simplify_response:
                    return response
                messages_left = response["item"]["throttling"][
                                    "maxNumUserMessagesInConversation"
                                ] - response["item"]["throttling"].get(
                    "numUserMessagesInConversation",
                    0,
                )
                if messages_left == 0:
                    raise Exception("Max messages reached")
                message = ""
                for msg in reversed(response["item"]["messages"]):
                    if msg.get("adaptiveCards") and msg["adaptiveCards"][0]["body"][
                        0
                    ].get("text"):
                        message = msg
                        break
                if not message:
                    raise Exception("No message found")
                suggestions = [
                    suggestion["text"]
                    for suggestion in message.get("suggestedResponses", [])
                ]
                adaptive_cards = message.get("adaptiveCards", [])
                adaptive_text = (
                    adaptive_cards[0]["body"][0].get("text") if adaptive_cards else None
                )
                sources = (
                    adaptive_cards[0]["body"][0].get("text") if adaptive_cards else None
                )
                sources_text = (
                    adaptive_cards[0]["body"][-1].get("text")
                    if adaptive_cards
                    else None
                )
                return {
                    "text": message["text"],
                    "author": message["author"],
                    "sources": sources,
                    "sources_text": sources_text,
                    "suggestions": suggestions,
                    "messages_left": messages_left,
                    "max_messages": response["item"]["throttling"][
                        "maxNumUserMessagesInConversation"
                    ],
                    "adaptive_text": adaptive_text,
                }
        return {}

    async def ask_stream(
            self,
            prompt: str,
            wss_link: str = "wss://sydney.bing.com/sydney/ChatHub",
            conversation_style: CONVERSATION_STYLE_TYPE = None,
            raw: bool = False,
            webpage_context: str | None = None,
            search_result: bool = False,
            locale: str = guess_locale(),
    ) -> Generator[bool, dict | str, None]:
        """
        Ask a question to the bot
        """
        async for response in self.chat_hub.ask_stream(
                prompt=prompt,
                conversation_style=conversation_style,
                wss_link=wss_link,
                raw=raw,
                webpage_context=webpage_context,
                search_result=search_result,
                locale=locale,
        ):
            yield response

    async def close(self) -> None:
        """
        Close the connection
        """
        await self.chat_hub.close()

    async def delete_conversation(
            self,
            conversation_id: str = None,
            conversation_signature: str = None,
            client_id: str = None,
    ) -> None:
        """
        Delete the chat in the server
        """
        await self.chat_hub.delete_conversation(
            conversation_id=conversation_id,
            conversation_signature=conversation_signature,
            client_id=client_id,
        )

    async def reset(self) -> None:
        """
        Reset the conversation
        """
        await self.close()
        self.chat_hub = ChatHub(
            await Conversation.create(self.proxy, cookies=self.chat_hub.cookies),
            proxy=self.proxy,
            cookies=self.chat_hub.cookies,
        )


if __name__ == "__main__":
    from .main import main

    main()
