import asyncio
import logging
from typing import Union

import discord
from redbot.core import Config, checks, commands
from redbot.core.bot import Red
from redbot.core.commands import Cog
from redbot.core.utils.chat_formatting import pagify

log = logging.getLogger("red.fox_v3.stealemoji")
# Replaced with discord.Asset.read()
# async def fetch_img(session: aiohttp.ClientSession, url: StrOrURL):
#     async with session.get(url) as response:
#         assert response.status == 200
#         return await response.read()


async def check_guild(guild, emoji):
    if len(guild.emojis) >= 2 * guild.emoji_limit:
        return False

    if len(guild.emojis) < guild.emoji_limit:
        return True

    if emoji.animated:
        return sum(e.animated for e in guild.emojis) < guild.emoji_limit
    else:
        return sum(not e.animated for e in guild.emojis) < guild.emoji_limit


class StealEmoji(Cog):
    """
    This cog steals emojis and creates servers for them
    """

    default_stolemoji = {
        "guildbank": None,
        "name": None,
        "require_colons": None,
        "managed": None,
        "guild_id": None,
        "animated": None,
        "saveid": None,
    }

    def __init__(self, red: Red):
        super().__init__()
        self.bot = red
        self.config = Config.get_conf(self, identifier=11511610197108101109111106105)
        default_global = {
            "stolemoji": {},
            "guildbanks": [],
            "autobanked_guilds": [],
            "on": False,
            "notify": 0,
            "autobank": False,
        }

        self.config.register_global(**default_global)

        self.is_on = None

    async def red_delete_data_for_user(self, **kwargs):
        """Nothing to delete"""
        return

    @commands.group()
    async def stealemoji(self, ctx: commands.Context):
        """
        Base command for this cog. Check help for the commands list.
        """
        pass

    @checks.is_owner()
    @stealemoji.command(name="clearemojis")
    async def se_clearemojis(self, ctx: commands.Context, confirm: bool = False):
        """Removes the history of all stolen emojis. Will not delete emojis from server banks"""
        if not confirm:
            await ctx.maybe_send_embed(
                "This will reset all stolen emoji data.\n"
                "If you want to continue, run this command again as:\n"
                "`[p]stealemoji clearemojis True`"
            )
            return

        await self.config.stolemoji.clear()
        await ctx.tick()

    @checks.is_owner()
    @stealemoji.command(name="print")
    async def se_print(self, ctx: commands.Context):
        """Prints all the emojis that have been stolen so far"""
        stolen = await self.config.stolemoji()
        id_list = [v.get("saveid") for k, v in stolen.items()]

        emoj = " ".join(str(e) for e in self.bot.emojis if e.id in id_list)

        if emoj == " ":
            await ctx.maybe_send_embed("No stolen emojis yet")
            return

        for page in pagify(emoj, delims=[" "]):
            await ctx.maybe_send_embed(page)

    @checks.is_owner()
    @stealemoji.command(name="notify")
    async def se_notify(self, ctx: commands.Context):
        """Cycles between notification settings for when an emoji is stolen

        None (Default)
        DM Owner
        Msg in server channel
        """
        curr_setting = await self.config.notify()

        if not curr_setting:
            await self.config.notify.set(1)
            await ctx.maybe_send_embed("Bot owner will now be notified when an emoji is stolen")
        elif curr_setting == 1:
            channel: discord.TextChannel = ctx.channel
            await self.config.notify.set(channel.id)
            await ctx.maybe_send_embed("This channel will now be notified when an emoji is stolen")
        else:
            await self.config.notify.set(0)
            await ctx.maybe_send_embed("Notifications are now off")

    @checks.is_owner()
    @stealemoji.command(name="collect")
    async def se_collect(self, ctx):
        """Toggles whether emoji's are collected or not"""
        curr_setting = await self.config.on()
        await self.config.on.set(not curr_setting)

        self.is_on = await self.config.on()

        await ctx.maybe_send_embed("Collection is now " + str(not curr_setting))

    @checks.is_owner()
    @stealemoji.command(name="autobank")
    async def se_autobank(self, ctx):
        """Toggles automatically creating new guilds as emoji banks"""
        curr_setting = await self.config.autobank()
        await self.config.autobank.set(not curr_setting)

        self.is_on = await self.config.autobank()

        await ctx.maybe_send_embed("AutoBanking is now " + str(not curr_setting))

    @checks.is_owner()
    @commands.guild_only()
    @stealemoji.command(name="deleteserver", aliases=["deleteguild"])
    async def se_deleteserver(self, ctx: commands.Context, guild_id=None):
        """Delete servers the bot is the owner of.

        Useful for auto-generated guildbanks."""
        if guild_id is None:
            guild = ctx.guild
        else:
            guild = await self.bot.get_guild(guild_id)

        if guild is None:
            await ctx.maybe_send_embed("Failed to get guild, cancelling")
            return
        guild: discord.Guild
        await ctx.maybe_send_embed(
            f"Will attempt to delete {guild.name} ({guild.id})\n" f"Okay to continue? (yes/no)"
        )

        def check(m):
            return m.author == ctx.author and m.channel == ctx.channel

        try:
            answer = await self.bot.wait_for("message", timeout=120, check=check)
        except asyncio.TimeoutError:
            await ctx.send("Timed out, canceling")
            return

        if answer.content.upper() not in ["Y", "YES"]:
            await ctx.maybe_send_embed("Cancelling")
            return
        try:
            await guild.delete()
        except discord.Forbidden:
            log.exception("No permission to delete. I'm probably not the guild owner")
            await ctx.maybe_send_embed("No permission to delete. I'm probably not the guild owner")
        except discord.HTTPException:
            log.exception("Unexpected error when deleting guild")
            await ctx.maybe_send_embed("Unexpected error when deleting guild")
        else:
            await self.bot.send_to_owners(f"Guild {guild.name} deleted")

    @checks.is_owner()
    @commands.guild_only()
    @stealemoji.command(name="bank")
    async def se_bank(self, ctx):
        """Add or remove current server as emoji bank"""

        def check(m):
            return (
                m.content.upper() in ["Y", "YES", "N", "NO"]
                and m.channel == ctx.channel
                and m.author == ctx.author
            )

        already_a_guildbank = ctx.guild.id in (await self.config.guildbanks())

        if already_a_guildbank:
            await ctx.maybe_send_embed(
                "This is already an emoji bank\n"
                "Are you sure you want to remove the current server from the emoji bank list? (y/n)"
            )
        else:
            await ctx.maybe_send_embed(
                "This will upload custom emojis to this server\n"
                "Are you sure you want to make the current server an emoji bank? (y/n)"
            )

        msg = await self.bot.wait_for("message", check=check)

        if msg.content.upper() in ["N", "NO"]:
            await ctx.maybe_send_embed("Cancelled")
            return

        async with self.config.guildbanks() as guildbanks:
            if already_a_guildbank:
                guildbanks.remove(ctx.guild.id)
            else:
                guildbanks.append(ctx.guild.id)

        if already_a_guildbank:
            await ctx.maybe_send_embed("This server has been removed from being an emoji bank")
        else:
            await ctx.maybe_send_embed("This server has been added to be an emoji bank")

    @commands.Cog.listener()
    async def on_reaction_add(self, reaction: discord.Reaction, user: discord.User):
        """Event handler for reaction watching"""
        if not reaction.custom_emoji:
            # print("Not a custom emoji")
            return

        if self.is_on is None:
            self.is_on = await self.config.on()

        if not self.is_on:
            # print("Collecting is off")
            return

        guild: discord.Guild = getattr(user, "guild", None)
        if await self.bot.cog_disabled_in_guild(self, guild):  # Handles None guild just fine
            return

        emoji: discord.Emoji = reaction.emoji
        if emoji in self.bot.emojis:
            # print("Emoji already in bot.emojis")
            return

        # This is now a custom emoji that the bot doesn't have access to, time to steal it
        # First, do I have an available guildbank?

        guildbank: Union[discord.Guild, None] = None
        banklist = await self.config.guildbanks()
        for guild_id in banklist:
            guild: discord.Guild = self.bot.get_guild(guild_id)
            # if len(guild.emojis) < 50:
            if await check_guild(guild, emoji):
                guildbank = guild
                break

        if guildbank is None:
            if not await self.config.autobank():
                return

            try:
                guildbank: discord.Guild = await self.bot.create_guild(
                    "StealEmoji Guildbank", code="S93bqTqKQ9rM"
                )
            except discord.HTTPException:
                await self.config.autobank.set(False)
                log.exception("Unable to create guilds, disabling autobank")
                return
            async with self.config.guildbanks() as guildbanks:
                guildbanks.append(guildbank.id)
            # Track generated guilds for easier deletion
            async with self.config.autobanked_guilds() as autobanked_guilds:
                autobanked_guilds.append(guildbank.id)

            await asyncio.sleep(2)

            if guildbank.text_channels:
                channel = guildbank.text_channels[0]
            else:
                # Always hits the else.
                # Maybe create_guild doesn't return guild object with
                #    the template channel?
                channel = await guildbank.create_text_channel("invite-channel")
            invite = await channel.create_invite()

            await self.bot.send_to_owners(invite)
            log.info(f"Guild created id {guildbank.id}. Invite: {invite}")
        # Next, have I saved this emoji before (because uploaded emoji != orignal emoji)

        if str(emoji.id) in await self.config.stolemoji():
            # print("Emoji has already been stolen")
            return

        img = await emoji.url.read()

        try:
            uploaded_emoji = await guildbank.create_custom_emoji(
                name=emoji.name, image=img, reason="Stole from " + str(user)
            )
        except discord.Forbidden as e:
            # print("PermissionError - no permission to add emojis")
            raise PermissionError("No permission to add emojis") from e
        except discord.HTTPException as e:
            # print("HTTPException exception")
            raise e  # Unhandled error

        # If you get this far, YOU DID IT

        save_dict = self.default_stolemoji.copy()
        # e_attr_list = [a for a in dir(emoji) if not a.startswith("__")]

        for k in save_dict.keys():
            save_dict[k] = getattr(emoji, k, None)

        # for k in e_attr_list:
        #     if k in save_dict:
        #         save_dict[k] = getattr(emoji, k, None)

        save_dict["guildbank"] = guildbank.id
        save_dict["saveid"] = uploaded_emoji.id

        async with self.config.stolemoji() as stolemoji:
            stolemoji[emoji.id] = save_dict

        # Enable the below if you want to get notified when it works
        notify_settings = await self.config.notify()
        if notify_settings:
            if notify_settings == 1:
                owner = await self.bot.application_info()
                target = owner.owner
            else:
                target = self.bot.get_channel(notify_settings)

            await target.send(f"Just added emoji {uploaded_emoji} to server {guildbank}")