"""
Contains code for trivia games.

*Classes*
---------
  Trivia
    Contains all the code for the trivia commands.
"""
import urllib  # Used to get data from api
import json  # Used to read data from api
import random  # Used to shuffle answers
import asyncio  # Used to sleep

from interactions import SlashContext  # Used for type hints


class Trivia():
  """
  Contains the code for trivia games.

  *Methods*
  ---------
    triviaStart(channel: str) -> str, str, str
    triviaAnswer(user: str, channel: str, command: str) -> str
    triviaCountPoints(channel: str)
    triviaParse(ctx: SlashContext, answer: str)
  """

  def __init__(self, bot):
    """Initialize the class."""
    self.bot = bot

  def triviaStart(self, channel: str):
    """
    Start a game of trivia.

    Downloads a question with answers, shuffles the wrong answers
    with the correct answer and returns the questions and answers.
    Also saves the question in the database.

    *Parameters*
    ------------
      channel: str
        The id of the channel to start the game in

    *Returns*
    ---------
      send_message: str
        The message to return to the user.
    """
    triviaQuestions = self.bot.database["trivia questions"]
    question = triviaQuestions.find_one({"_id": channel})

    self.bot.log(f"Trying to find a trivia question for {channel}")

    if question is None:
      apiUrl = "https://opentdb.com/api.php?amount=10&type=multiple"
      with urllib.request.urlopen(apiUrl) as response:
        data = json.loads(response.read())

      question = data["results"][0]["question"]
      self.bot.log(f"Found the question \"{question}\"")
      answers = data["results"][0]["incorrect_answers"]
      answers.append(data["results"][0]["correct_answer"])
      random.shuffle(answers)
      correctAnswer = data["results"][0]["correct_answer"]
      correctAnswer = answers.index(correctAnswer) + 97

      newQuestion = {
        "_id": channel,
        "answer": str(chr(correctAnswer)),
        "players": {}
      }
      triviaQuestions.insert_one(newQuestion)

      replacements = {
        "&#039;": "\'",
        "&quot;": "\"",
        "&ldquo;": "\"",
        "&rdquo;": "\"",
        "&eacute;": "é"
      }
      question = data["results"][0]["question"]

      for key, value in replacements.items():
        question = question.replace(key, value)

      for answer in answers:
        for key, value in replacements.items():
          answer = answer.replace(key, value)

      return question, answers, correctAnswer
    else:
      log_message = "There was already a trivia question for that channel"
      self.bot.log(log_message)
      return self.bot.long_strings["Trivia going on"], "", ""

  def triviaAnswer(self, user: str, channel: str, command: str):
    """
    Answer the current trivia question.

    *Parameters*
    ------------
      user: str
        The id of the user who answered.
      channel: str
        The id of the channel the game is in.
      command: str
        The user's answer.

    *Returns*
    ---------
      send_message: str
        The message to send if the function failed.
    """
    triviaQuestions = self.bot.database["trivia questions"]
    question = triviaQuestions.find_one({"_id": channel})

    if command not in ["a", "b", "c", "d"]:
      self.bot.log("I didn't quite understand that")
      return "I didn't quite understand that"
    elif question is None:
      self.bot.log("There's no question right now")
      return "There's no question right now"
    elif user in question["players"]:
      self.bot.log(f"{user} has already answered this question")
      return f"{user} has already answered this question"
    else:
      self.bot.log(f"{user} answered the question in {channel}")

      updater = {"$set": {f"players.{user}": command}}
      triviaQuestions.update_one({"_id": channel}, updater)
      return None

  def triviaCountPoints(self, channel: str):
    """
    Add money to every winner's account.

    *Parameters*
    ------------
      channel: str
        The id of the channel the game is in.
    """
    triviaQuestions = self.bot.database["trivia questions"]
    question = triviaQuestions.find_one({"_id": channel})

    self.bot.log("Counting points for question in "+channel)

    if question is not None:
      for player, answer in question["players"].items():
        if answer == question["answer"]:
          self.bot.money.addMoney(player, 1)
    else:
      self.bot.log("Couldn't find the questio")

    return None

  async def triviaParse(self, ctx: SlashContext, answer: str):
    """
    Parse a trivia command.

    *Parameters*
    ------------
      ctx: SlashContext
        The context of the command.
      answer: str
        The answer, if any.
    """
    await self.bot.defer(ctx)
    channelId = str(ctx.channel_id)
    if answer == "":
      question, options, correctAnswer = self.triviaStart(channelId)
      if options != "":
        results = "**"+question+"**\n"
        for x, option in enumerate(options):
          results += chr(x+97) + ") "+option+"\n"

        await ctx.send(results)

        await asyncio.sleep(60)

        self.triviaCountPoints(channelId)

        delete_gameParams = ["trivia questions", channelId]
        self.bot.database_funcs.delete_game(*delete_gameParams)

        self.bot.log("Time's up for the trivia question", channelId)
        send_message = self.bot.long_strings["Trivia time up"]
        format_parameters = [chr(correctAnswer), options[correctAnswer-97]]
        send_message = send_message.format(*format_parameters)
        await ctx.send(send_message)
      else:
        await ctx.send(question, hidden=True)

    elif answer in ["a", "b", "c", "d"]:
      userId = f"#{ctx.author.id}"
      response = self.triviaAnswer(userId, channelId, answer)
      if response is None:
        user_name = ctx.author.display_name
        await ctx.send(f"{user_name} answered **{answer}**")
      else:
        await ctx.send(response)
    else:
      self.bot.log("I didn't understand that", channelId)
      await ctx.send("I didn't understand that")
