"""Discord bot for handling user queries and interacting with an API.

This module contains the main functionality for a Discord bot that listens to user messages,
detects the language of the message, creates threads for user queries, interacts with an API to get responses,
formats the responses, and sends them back to the user. It also handles user feedback on the bot's responses.

"""

import asyncio
import logging
import uuid

import discord
from discord.ext import commands

from wandbot.api.client import AsyncAPIClient
from wandbot.apps.discord.config import DiscordAppConfig
from wandbot.apps.utils import format_response

from dotenv import load_dotenv
import pathlib
# Determine the project root directory (assuming it's 3 levels up from this script)
project_root = pathlib.Path(__file__).resolve().parents[3]
dotenv_path = project_root / ".env"

load_dotenv(dotenv_path=dotenv_path)

logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)

intents = discord.Intents.all()
intents.typing = False
intents.presences = False
intents.messages = True
intents.reactions = True

bot = commands.Bot(command_prefix="!", intents=intents)
config = DiscordAppConfig()
api_client = AsyncAPIClient(url=config.WANDBOT_API_URL)


@bot.event
async def on_message(message: discord.Message):
    """Handles the on_message event in Discord.

    Args:
        message: The message object received.

    Returns:
        None
    """
    if message.author == bot.user:
        return
    if bot.user is not None and bot.user.mentioned_in(message):
        mention = f"<@{message.author.id}>"
        thread = None
        is_following = None
        if isinstance(message.channel, discord.Thread):
            if (
                message.channel.parent.id == config.PROD_DISCORD_CHANNEL_ID
                or message.channel.parent.id == config.TEST_DISCORD_CHANNEL_ID
            ):
                thread = message.channel
                is_following = True
        else:
            if (
                message.channel.id == config.PROD_DISCORD_CHANNEL_ID
                or message.channel.id == config.TEST_DISCORD_CHANNEL_ID
            ):
                thread = await message.channel.create_thread(
                    name="Thread", type=discord.ChannelType.public_thread
                )  # currently calling it "Thread" because W&B Support makes it sound too official.
                is_following = False
        if thread is not None:
            if is_following:
                chat_history = await api_client.get_chat_history(
                    application=config.APPLICATION, thread_id=str(thread.id)
                )
            else:
                chat_history = None
            if not chat_history:
                await thread.send(
                    config.INTRO_MESSAGE.format(mention=mention),
                    mention_author=True,
                )

            response = await api_client.query(
                question=str(message.clean_content),
                chat_history=chat_history,
                language=config.bot_language,
                application=config.APPLICATION,
            )
            if response is None:
                await thread.send(
                    config.ERROR_MESSAGE.format(mention=mention),
                    mention_author=True,
                )
                return
            outro_message = config.OUTRO_MESSAGE
            sent_message = None
            if len(response.answer) > 1200:
                answer_chunks = []
                for i in range(0, len(response.answer), 1200):
                    answer_chunks.append(response.answer[i : i + 1200])
                for i, answer_chunk in enumerate(answer_chunks):
                    response_copy = response.model_copy()
                    response_copy.answer = answer_chunk
                    if i == len(answer_chunks) - 1:
                        sent_message = await thread.send(
                            format_response(
                                config,
                                response_copy,
                                outro_message,
                            ),
                        )
                    else:
                        sent_message = await thread.send(
                            format_response(
                                config,
                                response_copy,
                                "",
                                is_last=False,
                            ),
                        )
            else:
                sent_message = await thread.send(
                    format_response(
                        config,
                        response,
                        outro_message,
                    ),
                )
            if sent_message is not None:
                await api_client.create_question_answer(
                    thread_id=str(thread.id),
                    question_answer_id=str(sent_message.id),
                    question=response.question,
                    answer=response.answer,
                    system_prompt=response.system_prompt,
                    model=response.model,
                    sources=response.sources,
                    source_documents=response.source_documents,
                    total_tokens=response.total_tokens,
                    prompt_tokens=response.prompt_tokens,
                    completion_tokens=response.completion_tokens,
                    time_taken=response.time_taken,
                    start_time=response.start_time,
                    end_time=response.end_time,
                    api_call_statuses=response.api_call_statuses,
                    language=config.bot_language,
                )
                # # Add reactions for feedback
                await sent_message.add_reaction("👍")
                await sent_message.add_reaction("👎")

            # # Wait for reactions
            def check(user_reaction, author):
                return author == message.author and str(
                    user_reaction.emoji
                ) in [
                    "👍",
                    "👎",
                ]

            try:
                reaction, user = await bot.wait_for(
                    "reaction_add", timeout=config.WAIT_TIME, check=check
                )

            except asyncio.TimeoutError:
                # await thread.send("🤖")
                rating = 0

            else:
                # Get the feedback value
                if str(reaction.emoji) == "👍":
                    rating = 1
                elif str(reaction.emoji) == "👎":
                    rating = -1
                else:
                    rating = 0

            # # Send feedback to API
            # await api_client.create_feedback(
            #     feedback_id=str(uuid.uuid4()),
            #     question_answer_id=str(sent_message.id),
            #     rating=rating,
            # )

        await bot.process_commands(message)


if __name__ == "__main__":
    bot.run(config.DISCORD_BOT_TOKEN)
