import discord
from discord.ext import commands
from django.contrib.auth.models import User
from django.db import close_old_connections

from minecraft_manager.api import api
from minecraft_manager.bot.utils import get_application, build_info
from minecraft_manager.models import Application, Player


class Commands(commands.Cog):

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

    async def cog_before_invoke(self, ctx):
        # FIX STALE DB CONNECTIONS
        close_old_connections()

    async def cog_check(self, ctx):
        # No DMs
        if ctx.guild is None:
            return False

        # Check roles
        if not hasattr(ctx.author, "roles"):
            return False

        for role in ctx.author.roles:
            for auth_role in self.bot.auth_roles:
                if role.id == auth_role:
                    return True
        return False

    def is_superuser(self, member: discord.Member):
        for role in member.roles:
            for auth_role in self.bot.superuser_roles:
                if role.id == auth_role:
                    return True
        return False

    @commands.command()
    async def help(self, ctx):
        embed = discord.Embed(colour=discord.Colour(0x417505))
        embed.set_thumbnail(
            url="https://cdn.discordapp.com/avatars/454457830918062081/b5792489bc43d9e17b8f657880a17dd4.png")
        embed.add_field(name="Minecraft Manager Help", value="-----------------------------")
        embed.add_field(name="{}app search <username>".format(self.bot.prefix),
                        value="Search for applications by partial or exact username.")
        embed.add_field(name="{}app info <app ID>".format(self.bot.prefix),
                        value="Get detailed information about a specific application.")
        embed.add_field(name="{}app accept|deny <app ID>".format(self.bot.prefix), value="Take action on an application.")
        embed.add_field(name="{}demote <username>".format(self.bot.prefix),
                        value="Demote a player to the role given to accepted applications.")
        embed.add_field(name="{}compare".format(self.bot.prefix), value="Compare Discord users to the Whitelist.")
        await self.bot.discord_message(ctx.message.channel, embed)

    @commands.group("app", aliases=["application"])
    async def app(self, ctx):
        if ctx.invoked_subcommand is None:
            await self.bot.discord_message(ctx.message.channel, "No sub-command supplied. Info, Search, Accept, or Deny.")

    @commands.command()
    async def info(self, ctx, *args):
        await self._info(ctx, *args)

    @app.command("info")
    async def app_info(self, ctx, *args):
        await self._info(ctx, *args)

    async def _info(self, ctx, *args):
        if len(args) == 0:
            await self.bot.discord_message(ctx.message.channel, "Info requires an application ID or username.")

        key = args[0]
        is_id = True
        try:
            int(key)
        except:
            is_id = False

        if is_id:
            application = get_application(key)
            if not application:
                await self.bot.discord_message(ctx.message.channel, "An Application with that ID doesn't exist.")
                return
        else:
            found = False
            applications = Application.objects.filter(username__icontains=key)
            if len(applications) == 0:
                applications = Application.objects.filter(player__username__icontains=key)
                if len(applications) == 1:
                    await self.bot.discord_message(ctx.message.channel, "**No applications matched, however there is a player match**")
                    application = applications[0]
                    found = True
            elif len(applications) == 1:
                application = applications[0]
                found = True
            if not found:
                await self.bot.discord_message(ctx.message.channel, "An exact Application could not be found. Try search instead.")
                return
        await self.bot.discord_message(ctx.message.channel, build_info(application))

    @commands.command()
    async def accept(self, ctx, app_id: int):
        await self._accept(ctx, app_id)

    @app.command("accept")
    async def app_accept(self, ctx, app_id: int):
        await self._accept(ctx, app_id)

    async def _accept(self, ctx, app_id: int):
        application = get_application(app_id)
        if not application:
            await self.bot.discord_message(ctx.message.channel, "An Application with that ID doesn't exist.")
            return

        if not application.accepted:
            application.accepted = True
            application.save()
            if Player.objects.filter(username__iexact=application.username).exists():
                player = Player.objects.get(username__iexact=application.username)
                player.application_id = application.id
                player.save()
            await self.bot.discord_message(ctx.message.channel, "App ID **{0}** was successfully accepted.".format(app_id))
            if not api.plugin(api.PLUGIN_ACCEPT, application.username):
                await self.bot.discord_message(ctx.message.channel, "Could not accept in-game, is the server running?")

    @commands.command()
    async def deny(self, ctx, app_id: int):
        await self._deny(ctx, app_id)

    @app.command("deny")
    async def app_deny(self, ctx, app_id: int):
        await self._deny(ctx, app_id)

    async def _deny(self, ctx, app_id: int):
        application = get_application(app_id)
        if not application:
            await self.bot.discord_message(ctx.message.channel, "An Application with that ID doesn't exist.")
            return

        if not application.accepted:
            application.accepted = False
            application.save()
            if Player.objects.filter(username__iexact=application.username).exists():
                player = Player.objects.get(username__iexact=application.username)
                player.application_id = application.id
                player.save()
            await self.bot.discord_message(ctx.message.channel, "App ID **{0}** was successfully denied.".format(app_id))
            if not api.plugin(api.PLUGIN_DENY, application.username):
                await self.bot.discord_message(ctx.message.channel, "Could not deny in-game, is the server running?")

    @commands.command()
    async def search(self, ctx, search: str):
        await self._search(ctx, search)

    @app.command("search")
    async def app_search(self, ctx, search: str):
        await self._search(ctx, search)

    async def _search(self, ctx, search: str):
        applications = Application.objects.filter(username__icontains=search)[:10]
        count = Application.objects.filter(username__icontains=search).count()
        if count > 0:
            if count == 1:
                info = build_info(applications[0])
            else:
                info = "**Found the following applications**"
                for app in applications:
                    info += "\n{0} - {1} ({2})".format(app.id, app.username.replace("_", "\\_"), app.status)
                if count > 10:
                    info += "\n**This is only 10 applications out of {0} found. Please narrow your search if possible.**".format(len(applications))
        else:
            players = Player.objects.filter(username__icontains=search, application__isnull=False)[:10]
            count = Player.objects.filter(username__icontains=search, application__isnull=False).count()
            if count > 0:
                if count == 1:
                    await self.bot.discord_message(ctx.message.channel, "**No applications matched, however there is a player match**")
                    info = build_info(players[0].application)
                else:
                    info = "**No applications matched, however there are player matches**"
                    for player in players:
                        app = player.application
                        info += "\n{0} - {1} AKA {2} ({3})".format(app.id, app.username.replace("_", "\\_"),
                                                                   player.username.replace("_", "\\_"), app.status)
                    if count > 10:
                        info += "\n**This is only 10 players out of {0} found. Please narrow your search if possible.**".format(len(players))
            else:
                info = "No applications matched that search."
        await self.bot.discord_message(ctx.message.channel, info)

    @commands.command()
    async def demote(self, ctx, username: str):
        if not self.is_superuser(ctx.author):
            return
        await ctx.message.delete()
        if api.plugin(api.PLUGIN_DEMOTE, username):
            await self.bot.discord_message(ctx.message.channel, "{} has been demoted in-game.".format(username))
        else:
            await self.bot.discord_message(ctx.message.channel, "{} could not be demoted in-game, is the server running?".format(username))
        if User.objects.filter(username__iexact=username).exists():
            user = User.objects.get(username__iexact=username)
            user.is_active = False
            user.save()
            await self.bot.discord_message(ctx.message.channel, "{} has been de-activated in MCM.".format(username))
        else:
            await self.bot.discord_message(ctx.message.channel, "{} could not be found in MCM, is their account up-to-date?".format(username))

    @commands.command()
    async def compare(self, ctx):
        await ctx.message.delete()
        await ctx.message.channel.trigger_typing()
        no_player = []
        no_application = []
        for member in ctx.message.guild.members:
            if member.bot:
                continue
            name = member.nick if member.nick else member.name
            try:
                Player.objects.get(username__iexact=name)
            except:
                no_player.append(name)
                try:
                    Application.objects.get(username__iexact=name)
                except:
                    no_player = no_player[:-1]
                    no_application.append(name)
        if no_player:
            header = "**The following users have an application match, but no player match on the whitelist:**\n"
            await self.bot.discord_message(ctx.author, "{}```{}```".format(header, "\n".join(no_player)))
        if no_application:
            header = "**The following users do not have an application or player match on the whitelist:**\n"
            await self.bot.discord_message(ctx.author, "{}```{}```".format(header, "\n".join(no_application)))


def setup(bot):
    bot.add_cog(Commands(bot))
