"""

    AUTHOR:

        Khushal P Soonderji

    DATE:

        Wednesday, 15th Jan., 2025.

    OBJECTIVE:

        To handle all WhatsApp-related behaviour for Nimbus IT's service from one place.

    REFERENCES:

        N/A

    DOWNLOADS:

        N/A

"""


#   *****************************************************************************************************************
#  *****                                                                                                          ****
# ***                                                   IMPORT                                                      ***
#  *****                                                                                                          ****
#   *****************************************************************************************************************


# To make sibling directories accessible for imports:
import sys
sys.path.append(".")
sys.path.append("..")

# My async utils:
from utils_v2.date_time import date_time
from utils_v2.database.async_mysql_v2 import AsyncMySQL
from utils_v2.database.async_mongo_v2 import AsyncMongo
from utils_v2.cache.async_redis_cache_v2 import AsyncRedisCache

# Controllers:
from controllers_v2.message.chat.base import ChatController

# Models:
from models.core.auth_token import CoreAuthTokenModel
from models.core.message import CoreMessageModel
from models.message.chat.send import (
    NimbusWhatsAppMessage,
    ChatSendOneResult,
    ChatSendManyResults
)

# Chat clients:
from utils_v2.whatsapp.nimbus.controllers.async_nimbus_whatsapp import AsyncNimbusWhatsapp

# To work with datatypes:
from typing import List, Any

# To make HTTP requests:
import httpx

# For asynchronous activities:
import asyncio


#   *****************************************************************************************************************
#  *****                                                                                                          ****
# ***                                           MACROS / ONE-TIME INIT                                              ***
#  *****                                                                                                          ****
#   *****************************************************************************************************************


# --- Nothing Yet


#   *****************************************************************************************************************
#  *****                                                                                                          ****
# ***                                                VARIABLES                                                      ***
#  *****                                                                                                          ****
#   *****************************************************************************************************************


# --- Nothing Yet


#   *****************************************************************************************************************
#  *****                                                                                                          ****
# ***                                                  FUNCTIONS                                                    ***
#  *****                                                                                                          ****
#   *****************************************************************************************************************


# --- Nothing Yet


#   *****************************************************************************************************************
#  *****                                                                                                          ****
# ***                                                   CLASSES                                                     ***
#  *****                                                                                                          ****
#   *****************************************************************************************************************


class WhatsAppNimbusController(ChatController):

    # ┏┓
    # ┃ ┏┓┏┓┏╋┏┓┓┏┏╋┏┓┏┓
    # ┗┛┗┛┛┗┛┗┛ ┗┻┗┗┗┛┛

    def __init__(
            self,
            cache: AsyncRedisCache = None,
            http_client: httpx.AsyncClient = None,
            alert_url: str = None,
            debug: bool = True,
            debug_prefix: str = "WhatsApp Nimbus (C) | ",
            debug_only_errors: bool = True
    ):

        """
        This is the controller for Nimbus IT's WhatsApp service.
        :param cache: The object to use for caching results from database calls.
        :param http_client: The HTTP client
        :param debug: Whether, or not, you would like to print debugging messages:
        :param debug_prefix: The prefix to print with the debugging messages.
        :param debug_only_errors: Whether you would like to print only error messages or all messages.
        :return: None.
        """

        # Invoke the parent's constructor:
        super().__init__(
            cache = cache,
            alert_url = alert_url,
            http_client = http_client,
            base_filter = {"client": "whatsappNimbus"},
            debug = debug,
            debug_prefix = debug_prefix,
            debug_only_errors = debug_only_errors
        )

    # ┏┓     ┓  ┳┳┓
    # ┗┓┏┓┏┓┏┫  ┃┃┃┏┓┏┏┏┓┏┓┏┓┏
    # ┗┛┗ ┛┗┗┻  ┛ ┗┗ ┛┛┗┻┗┫┗ ┛
    #                     ┛

    async def send_one_message(
            self,
            sql_conn: AsyncMySQL,
            mongo_data_conn: AsyncMongo,
            auth_token: CoreAuthTokenModel,
            client: AsyncNimbusWhatsapp,
            message: NimbusWhatsAppMessage,
            tags: List[Any]
    ) -> ChatSendOneResult:

        """
        To send one message from the third-party client.
        :param sql_conn: The connection to the database to use for this operation.
        :param mongo_data_conn: The connection to the database to use for this operation.
        :param auth_token: The auth-token model for the account from which the message has to be sent.
        :param client: The connection/instance of the third-party client to use to perform this operation.
        :param message: The message that you want to send to the recipient.
        :param tags: Any tags that you would like to attach to the message. To be used later for internal filtering.
        :return: The structured response model to describe the operation.
        """

        # Send the SMS:
        client_response = await client.send_whatsapp(
            recipient_number = message.recipientNo,
            message = message.message,
            pdf_url = message.pdfUrl,
            image_0_url = message.image0Url,
            image_1_url = message.image1Url,
            schedule_on = message.scheduleTs
        )

        # Convert the format of the SMS client's response to the core message model.
        sent_message_model = CoreMessageModel(
            ts = client_response.ts,
            syncTs = date_time.get_current_utc_date_time(as_string = False),
            tokenId = auth_token.authTokenId,
            serviceType = auth_token.serviceType,
            client = auth_token.client,
            clientMessageId = client_response.requestId,
            clientThreadId = message.recipientNo,
            isSent = True,
            isBroadcast = False,
            sentSuccessfully = client_response.success,
            sender = auth_token.clientUserId["senderId"],
            recipient = message.recipientNo,
            chat = message.recipientNo,
            message = client_response.model_dump(),
            snippet = message.message,
            aiSnippet = None,
            tags = list(set(tags + ["Chat", "Nimbus", "WhatsApp"]))
        )

        # Save the result to the database:
        message_id = await self.save_one_message(
            mongo_data_conn = mongo_data_conn,
            message = sent_message_model
        )
        self._printer(message_id, client_response.success)

        # Done here:
        success = True if client_response.success and message_id else False
        return ChatSendOneResult(
            success = success,
            message = "Message sent successfully." if client_response.success else "Message sending failed.",
            chatMessage = sent_message_model
        )

    async def send_many_messages(
            self,
            sql_conn: AsyncMySQL,
            mongo_data_conn: AsyncMongo,
            http_client: httpx.AsyncClient,
            auth_token: CoreAuthTokenModel,
            client: AsyncNimbusWhatsapp | None,
            messages: List[NimbusWhatsAppMessage],
            tags: List[Any]
    ) -> ChatSendManyResults:

        """
        To send many chat messages in one go.
        :param sql_conn: The connection to the database to use for this operation.
        :param mongo_data_conn: The connection to the database to use for this operation.
        :param http_client: An HTTP client to use to make API calls through the third-party client's class.
        :param auth_token: The auth-token model for the account from which the message has to be sent.
        :param client: The connection/instance of the third-party client to use to perform this operation.
        :param messages: The messages that you want to send to the recipients.
        :param tags: Any tags that you would like to attach to the message. To be used later for internal filtering.
        :return: The structured response model to describe the operation.
        """

        # Start with a blank variable:
        cumulative_results = ChatSendManyResults()

        # Make the client from the auth-token:
        client = client or AsyncNimbusWhatsapp(
            api_key = auth_token.auth["apiKey"],
            http_client = self._http_client,
            debug = False
        )

        # Create and fire all the message-sending tasks:
        tasks = [
            self.send_one_message(
                sql_conn = sql_conn,
                mongo_data_conn = mongo_data_conn,
                auth_token = auth_token,
                client = client,
                message = message,
                tags = tags
            )
            for message in messages
        ]
        individual_results = await asyncio.gather(*tasks)

        # Prepare the final result:
        for result in individual_results:
            if result.success: cumulative_results.successCount += 1
            else: cumulative_results.failureCount += 1
            cumulative_results.totalCount += 1
            cumulative_results.chatMessages.append(result.chatMessage)
        cumulative_results.message = f"{cumulative_results.successCount}/{cumulative_results.totalCount} messgae(s) sent."

        # Done here:
        return cumulative_results


#   *****************************************************************************************************************
#  *****                                                                                                          ****
# ***                                               MAIN PROGRAM                                                    ***
#  *****                                                                                                          ****
#   *****************************************************************************************************************


if __name__ == "__main__":

    pass
