#   _____                         _
#  / ____|                       (_)
# | (___   ___  __ _ _____      ___ _ __ ___  _ __ ___   ___ _ __
#  \___ \ / _ \/ _` / __\ \ /\ / / | '_ ` _ \| '_ ` _ \ / _ \ '__|
#  ____) |  __/ (_| \__ \\ V  V /| | | | | | | | | | | |  __/ |
# |_____/ \___|\__,_|___/ \_/\_/ |_|_| |_| |_|_| |_| |_|\___|_|

import asyncio
from contextlib import suppress
from typing import Any, Optional, Union

import discord
from redbot.core import commands
from redbot.core.utils import chat_formatting, common_filters


class Nerdify(commands.Cog):
    """Nerdify your text."""

    __author__ = "SeaswimmerTheFsh"
    __version__ = "1.3.2"

    def __init__(self, bot):
        self.bot = bot

    @commands.command(aliases=["nerd"])
    async def nerdify(
        self, ctx: commands.Context, *, text: Optional[str] = None
    ) -> None:
        """Nerdify the replied to message, previous message, or your own text."""
        if not text:
            if hasattr(ctx.message, "reference") and ctx.message.reference:
                with suppress(
                    discord.Forbidden, discord.NotFound, discord.HTTPException
                ):
                    message_id = ctx.message.reference.message_id
                    if message_id:
                        text = (await ctx.fetch_message(message_id)).content
            if not text:
                messages = [message async for message in ctx.channel.history(limit=2)]
                # [0] is the command, [1] is the message before the command
                text = messages[1].content
        if text == "":
            await ctx.send(chat_formatting.error("I can't translate that!"))
            return
        await self.type_message(
            ctx.channel,
            self.nerdify_text(text),
            allowed_mentions=discord.AllowedMentions(
                everyone=False, users=False, roles=False
            ),
        )

    def nerdify_text(self, text: str) -> str:
        """Convert text to nerd speak.

        Args:
            text: The text to convert.

        Returns:
            The converted text."""
        return f'"{text}" 🤓'

    async def type_message(
        self, destination: discord.abc.Messageable, content: str, **kwargs: Any
    ) -> Union[discord.Message, None]:
        """Simulate typing and sending a message to a destination.

        Will send a typing indicator, wait a variable amount of time based on the length
        of the text (to simulate typing speed), then send the message.

        Args:
            destination: The [destination](https://discordpy.readthedocs.io/en/stable/api.html#discord.abc.Messageable) to send the message to.
            content: The content of the message to send.
            **kwargs: Any keyword arguments to pass to the [destination.send](https://discordpy.readthedocs.io/en/stable/api.html#discord.TextChannel.send) function.

        Returns:
            The message sent, or None if an error occurred.
        """
        content = common_filters.filter_urls(content)
        with suppress(discord.HTTPException):
            async with destination.typing():
                await asyncio.sleep(max(0.25, min(2.5, len(content) * 0.01)))
            return await destination.send(content=content, **kwargs)