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


# Cog: Any = getattr(commands, "Cog", object)
# listener = getattr(commands.Cog, "listener", None)  # Trusty + Sinbad
# if listener is None:
#     def listener(name=None):
#         return lambda x: x


class InfoChannel(Cog):
    """
    Create a channel with updating server info

    Less important information about the cog
    """

    def __init__(self, bot: Red):
        self.bot = bot
        self.config = Config.get_conf(
            self, identifier=731101021116710497110110101108, force_registration=True
        )

        default_guild = {
            "channel_id": None,
            "botchannel_id": None,
            "onlinechannel_id": None,
            "member_count": True,
            "bot_count": False,
            "online_count": False,
        }

        self.config.register_guild(**default_guild)

    @commands.command()
    @checks.admin()
    async def infochannel(self, ctx: commands.Context):
        """
        Toggle info channel for this server
        """

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

        guild: discord.Guild = ctx.guild
        channel_id = await self.config.guild(guild).channel_id()
        if channel_id is not None:
            channel: discord.VoiceChannel = guild.get_channel(channel_id)
        else:
            channel: discord.VoiceChannel = None

        if channel_id is not None and channel is None:
            await ctx.send("Info channel has been deleted, recreate it?")
        elif channel_id is None:
            await ctx.send("Enable info channel on this server?")
        else:
            await ctx.send("Do you wish to delete current info channels?")

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

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

        if channel is None:
            await self.make_infochannel(guild)
        else:
            await self.delete_infochannel(guild, channel)

        if not await ctx.tick():
            await ctx.send("Done!")

    @commands.group()
    @checks.admin()
    async def infochannelset(self, ctx: commands.Context):
        """
        Toggle different types of infochannels
        """

    @infochannelset.command(name="botcount")
    async def _infochannelset_botcount(self, ctx: commands.Context, enabled: bool = None):
        """
        Toggle an infochannel that shows the amount of bots in the server
        """
        guild = ctx.guild
        if enabled is None:
            enabled = not await self.config.guild(guild).bot_count()
        await self.config.guild(guild).bot_count.set(enabled)
        if enabled:
            await ctx.send("InfoChannel for bot count has been enabled.")
        else:
            await ctx.send("InfoChannel for bot count has been disabled.")

    @infochannelset.command(name="onlinecount")
    async def _infochannelset_onlinecount(self, ctx: commands.Context, enabled: bool = None):
        """
        Toggle an infochannel that shows the amount of online users in the server
        """
        guild = ctx.guild
        if enabled is None:
            enabled = not await self.config.guild(guild).online_count()
        await self.config.guild(guild).online_count.set(enabled)
        if enabled:
            await ctx.send("InfoChannel for online user count has been enabled.")
        else:
            await ctx.send("InfoChannel for online user count has been disabled.")

    async def make_infochannel(self, guild: discord.Guild):
        botcount = await self.config.guild(guild).bot_count()
        onlinecount = await self.config.guild(guild).online_count()
        overwrites = {
            guild.default_role: discord.PermissionOverwrite(connect=False),
            guild.me: discord.PermissionOverwrite(manage_channels=True, connect=True),
        }

        channel = await guild.create_voice_channel(
            "Placeholder", reason="InfoChannel make", overwrites=overwrites
        )
        await self.config.guild(guild).channel_id.set(channel.id)

        if botcount:
            botchannel = await guild.create_voice_channel(
                "Placeholder", reason="InfoChannel botcount", overwrites=overwrites
            )
            await self.config.guild(guild).botchannel_id.set(botchannel.id)
        if onlinecount:
            onlinechannel = await guild.create_voice_channel(
                "Placeholder", reason="InfoChannel onlinecount", overwrites=overwrites
            )
            await self.config.guild(guild).onlinechannel_id.set(onlinechannel.id)

        await self.update_infochannel(guild)

    async def delete_infochannel(self, guild: discord.Guild, channel: discord.VoiceChannel):
        guild_data = await self.config.guild(guild).all()
        botchannel_id = guild_data["botchannel_id"]
        onlinechannel_id = guild_data["onlinechannel_id"]
        botchannel: discord.VoiceChannel = guild.get_channel(botchannel_id)
        onlinechannel: discord.VoiceChannel = guild.get_channel(onlinechannel_id)
        channel_id = guild_data["channel_id"]
        channel: discord.VoiceChannel = guild.get_channel(channel_id)
        await channel.delete(reason="InfoChannel delete")
        if botchannel_id is not None:
            await botchannel.delete(reason="InfoChannel delete")
        if onlinechannel_id is not None:
            await onlinechannel.delete(reason="InfoChannel delete")
        await self.config.guild(guild).clear()

    async def update_infochannel(self, guild: discord.Guild):
        guild_data = await self.config.guild(guild).all()
        botcount = guild_data["bot_count"]
        onlinecount = guild_data["online_count"]

        # Gets count of bots
        # bots = lambda x: x.bot
        # def bots(x): return x.bot

        num = len([m for m in guild.members if m.bot])
        bot_msg = f"Bots: {num}"

        # Gets count of online users
        members = guild.member_count
        offline = len(list(filter(lambda m: m.status is discord.Status.offline, guild.members)))
        num = members - offline
        online_msg = f"Online: {num}"

        # Gets count of actual users
        total = lambda x: not x.bot
        num = len([m for m in guild.members if total(m)])
        human_msg = f"Total Humans: {num}"

        channel_id = guild_data["channel_id"]
        if channel_id is None:
            return

        botchannel_id = guild_data["botchannel_id"]
        onlinechannel_id = guild_data["onlinechannel_id"]
        channel_id = guild_data["channel_id"]
        channel: discord.VoiceChannel = guild.get_channel(channel_id)
        botchannel: discord.VoiceChannel = guild.get_channel(botchannel_id)
        onlinechannel: discord.VoiceChannel = guild.get_channel(onlinechannel_id)

        if guild_data["member_count"]:
            name = "{} ".format(human_msg)
            await channel.edit(reason="InfoChannel update", name=name)

        if botcount:
            name = "{} ".format(bot_msg)
            await botchannel.edit(reason="InfoChannel update", name=name)

        if onlinecount:
            name = "{} ".format(online_msg)
            await onlinechannel.edit(reason="InfoChannel update", name=name)

    @Cog.listener()
    async def on_member_join(self, member: discord.Member):
        await self.update_infochannel(member.guild)

    @Cog.listener()
    async def on_member_remove(self, member: discord.Member):
        await self.update_infochannel(member.guild)

    @Cog.listener()
    async def on_member_update(self, before: discord.Member, after: discord.Member):
        onlinecount = await self.config.guild(after.guild).online_count()
        if onlinecount:
            if before.status != after.status:
                await self.update_infochannel(after.guild)