"""
Classes used to handle bot events and errors.

*Classes*
---------
  EventHandler
  ErrorHandler
"""
import traceback  # Used to get the traceback of errors

import discord  # Used to init discord.Game and discord.Status, as well
        # as compare errors to discord errors and as typehints
from discord.ext import commands  # Used to compare errors with command
                  # errors

from interactions import SlashContext, ComponentContext
from gwendolyn_old.utils.util_functions import decode_id
from gwendolyn_old.exceptions import InvalidInteraction


class EventHandler():
  """
  Handles bot events.

  *Methods*
  ---------
    on_ready()
    on_slash_command(ctx: interactions.SlashContext)
    on_reaction_add(ctx: interactions.SlashContext)
  """

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

  async def on_ready(self):
    """Log and sets status when it logs in."""
    await self.bot.database_funcs.imdb_commands()
    name = self.bot.user.name
    userid = str(self.bot.user.id)
    logged_in_message = f"Logged in as {name}, {userid}"
    self.bot.log(logged_in_message, level=25)
    game = discord.Game("Use /help for commands")

    online_status = discord.Status.online
    await self.bot.change_presence(activity=game, status=online_status)

  async def on_slash_command(self, ctx: SlashContext):
    """Log when a slash command is given."""
    if ctx.subcommand_name is not None:
      subcommand = f" {ctx.subcommand_name} "
    else:
      subcommand = " "

    if ctx.subcommand_group is not None:
      sub_command_group = f"{ctx.subcommand_group} "
    else:
      sub_command_group = ""

    args = " ".join([str(i) for i in ctx.args])
    full_command = f"/{ctx.command}{subcommand}{sub_command_group}{args}"
    log_message = f"{ctx.author.display_name} ran {full_command}"
    self.bot.log(log_message, str(ctx.channel_id), level=25)

  async def on_component(self, ctx: ComponentContext):
    """Handle component interaction."""
    info = decode_id(ctx.custom_id)
    self.bot.log(f"Component action with info {info}")
    channel = ctx.channel
    author = str(ctx.author_id)

    if info[0].lower() == "plex":
      if info[1].lower() == "movie":
        await self.bot.other.plex.add_movie(
          ctx.origin_message,
          info[2],
          not isinstance(channel, discord.DMChannel)
        )
        return

      elif info[1].lower() == "show":
        await self.bot.other.plex.add_show(
          ctx.origin_message,
          info[2],
          not isinstance(channel, discord.DMChannel)
        )
      else:
        raise InvalidInteraction(ctx.custom_id, info)

    elif info[0].lower() == "hangman" and author == info[2]:
      if info[1].lower() == "guess":
        await self.bot.games.hangman.guess(ctx, *info[3:])
      elif info[1].lower() == "end":
        await self.bot.games.hangman.stop(ctx, *info[3:])
      else:
        raise InvalidInteraction(ctx.custom_id, info)
    elif info[0].lower() == "connectfour":
      connect_four = self.bot.games.connect_four
      if info[1].lower() == "place" and author == info[2]:
        await connect_four.place_piece(
          ctx,
          info[3],
          int(info[4]),
                    [int(info[5]), int(info[6])],
                    int(info[7]),
                    ctx.author_id,
                    int(info[8])
                )
            elif info[1].lower() == "end" and author in [info[2], info[3]]:
                await connect_four.surrender(
                    ctx, [int(info[2]), int(info[3])], info[4], info[5]
                )
            else:
                raise InvalidInteraction(ctx.custom_id, info)
        elif info[0].lower() == "blackjack":
            await self.bot.games.blackjack.decode_interaction(ctx, info[1:])
        else:
            raise InvalidInteraction(ctx.custom_id, info)



class ErrorHandler():
    """
    Handles errors.

    *Methods*
    ---------
        on_slash_command_error(ctx: interactions.SlashContext,
                               error: Exception)
        on_error(method: str)
    """

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

    async def on_slash_command_error(self, ctx: SlashContext,
                                     error: Exception):
        """Log when there's a slash command."""
        if isinstance(error, commands.CommandNotFound):
            await ctx.send("That's not a command")
        elif isinstance(error, discord.errors.NotFound):
            self.bot.log("Deleted message before I could add all reactions")
        elif isinstance(error, commands.errors.MissingRequiredArgument):
            self.bot.log(f"{error}", str(ctx.channel_id))
            await ctx.send(self.bot.long_strings["missing parameters"])
        else:
            params = [type(error), error, error.__traceback__]
            exception = traceback.format_exception(*params)

            exception_string = "".join(exception)
            log_messages = [f"exception in /{ctx.name}", f"{exception_string}"]
            self.bot.log(log_messages, str(ctx.channel_id), 40)
            if isinstance(error, discord.errors.NotFound):
                self.bot.log("Context is non-existant", level=40)
            else:
                await ctx.send("Something went wrong")

    async def on_error(self, method: str):
        """Log when there's an error."""
        exception = traceback.format_exc()

        exception_string = "".join(exception)
        log_messages = [f"exception in {method}", f"{exception_string}"]
        self.bot.log(log_messages, level=40)
