"""
The PDA ("main" class) for the :mod:`royalnet_telethon` frontend.
"""

from __future__ import annotations
import royalnet.royaltyping as t

import logging
import royalnet.engineer as engi
import enum
import discord as d

from .bullet.projectiles import DiscordMessageReceived, DiscordMessageEdited, DiscordMessageDeleted

log = logging.getLogger(__name__)


class DiscordpyPDAMode(enum.Enum):
    """
    .. todo:: Document this.
    """

    GLOBAL = enum.auto()
    CHANNEL = enum.auto()
    USER = enum.auto()
    CHANNEL_USER = enum.auto()


class DiscordpyPDAImplementation(engi.ConversationListImplementation):
    """
    .. todo:: Document this.
    """

    @property
    def namespace(self):
        return "discordpy"

    def __init__(self, name: str, bot_token: str,
                 mode: DiscordpyPDAMode = DiscordpyPDAMode.CHANNEL_USER):

        super().__init__(name=name)

        self.mode: DiscordpyPDAMode = mode
        """
        The mode to use for mapping dispensers.
        """

        self.bot_token: str = bot_token
        """
        .. todo:: Document this.
        """

        self.client: d.Client = d.Client()
        """
        .. todo:: Document this.        
        """

    def _register_events(self):
        """
        .. todo:: Document this.
        """

        self.log.info("Registering Discord.py events...")
        self.log.debug("Registering on_message...")
        self.client.on_message = self._on_message
        self.log.debug("Registering on_message_edit...")
        self.client.on_message_edit = self._on_message_edit
        self.log.debug("Registering on_message_delete...")
        self.client.on_message_delete = self._on_message_delete

    def _determine_key(self, message: d.Message):
        """
        .. todo:: Document this.
        """

        if self.mode == DiscordpyPDAMode.GLOBAL:
            return None
        elif self.mode == DiscordpyPDAMode.USER:
            author: d.User = message.author
            return author.id
        elif self.mode == DiscordpyPDAMode.CHANNEL:
            channel: t.Union[d.DMChannel, d.TextChannel] = message.channel
            return channel.id
        elif self.mode == DiscordpyPDAMode.CHANNEL_USER:
            author: d.User = message.author
            channel: t.Union[d.DMChannel, d.TextChannel] = message.channel
            return author.id, channel.id
        else:
            raise TypeError("Invalid mode")

    async def _on_message(self, message: d.Message):
        """
        .. todo:: Document this.
        """

        await self.put(
            key=self._determine_key(message=message),
            projectile=DiscordMessageReceived(event=message)
        )

    async def _on_message_edit(self, message: d.Message):
        """
        .. todo:: Document this.
        """

        await self.put(
            key=self._determine_key(message=message),
            projectile=DiscordMessageEdited(event=message)
        )

    async def _on_message_delete(self, message: d.Message):
        """
        .. todo:: Document this.
        """

        await self.put(
            key=self._determine_key(message=message),
            projectile=DiscordMessageDeleted(event=message)
        )

    async def run(self) -> t.NoReturn:
        await self.client.login(token=self.bot_token)
        self._register_events()
        await self.client.connect(reconnect=True)


__all__ = (
    "DiscordpyPDAImplementation",
)
