#create your views here.
# https://api.mojang.com/users/profiles/minecraft/<username>
from __future__ import absolute_import

import json, datetime, pytz, os, sys
from django.utils import timezone
from itertools import chain
from django.http import JsonResponse
from django.shortcuts import render, reverse, redirect
from django.views.decorators.csrf import csrf_protect
from django.utils.decorators import method_decorator
from django.conf import settings
from django.views.generic import View
from django.contrib.auth.models import User
from minecraft_manager.models import Application as AppModel, Player as PlayerModel, Ticket as TicketModel, TicketNote as TicketNoteModel, Note as NoteModel, IP as IPModel, Alert as AlertModel, UserSettings as UserSettingsModel
from minecraft_manager.forms import TicketNoteForm, NoteForm
from minecraft_manager.overview import overview_data
from minecraft_manager.utils import resolve_player
import minecraft_manager.api.api as API
from minecraft_manager.bot import Bot
from minecraft_manager.bot.discord import start as discord_start, stop as discord_stop, restart as discord_restart, \
    status as discord_status, display as discord_display


class Overview(View):

    def get(self, request):
        x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
        if x_forwarded_for:
            user_ip = x_forwarded_for.split(',')[0]
        else:
            user_ip = request.META.get('REMOTE_ADDR')
        try:
            request.user.usersettings.last_ip = user_ip
        except:
            request.user.usersettings = UserSettingsModel(auth_user=request.user)
            request.user.usersettings.last_ip = user_ip
        request.user.usersettings.save()


        return render(request, 'minecraft_manager/overview.html', {'current_app': 'overview', 'data': overview_data()})


class CoreProtect(View):

    def get(self, request):
        #http://www.24carrotcraft.com/assets/cp/index.php?username=etzelia
        return render(request, 'minecraft_manager/coreprotect.html', {'current_app': 'coreprotect'})


class Activity(View):

    def get(self, request):
        #http://www.24carrotcraft.com/assets/cp/activity.php?username=etzelia
        return render(request, 'minecraft_manager/activity.html', {'current_app': 'activity'})


class Ban(View):

    def get(self, request):
        ban_file = os.path.join(settings.MINECRAFT_BASE_DIR, 'banned-players.json')
        with open(ban_file, encoding='utf-8') as f:
            bans = json.load(f)
        for idx, ban in enumerate(bans):
            ban['source'] = API.strip_format(ban['source'])
            if PlayerModel.objects.filter(uuid=ban['uuid']).exists():
                ban['id'] = PlayerModel.objects.get(uuid=ban['uuid']).id
            else:
                ban['id'] = 0
            ban['source'] = API.strip_format(ban['source'])
            if ban['expires'] == "forever":
                ban['expires'] = "Permanent"
            else:
                dt = datetime.datetime.strptime(ban['expires'], "%Y-%m-%d %H:%M:%S %z")
                dt = dt.astimezone(pytz.timezone(request.user.usersettings.default_timezone))
                ban['expires'] = dt.strftime("%m/%d/%y %I:%M %p")
            dt = datetime.datetime.strptime(ban['created'], "%Y-%m-%d %H:%M:%S %z")
            dt = dt.astimezone(pytz.timezone(request.user.usersettings.default_timezone))
            ban['created'] = dt.strftime("%m/%d/%y %I:%M %p")
            bans[idx] = ban
        bans = sorted(bans, key=lambda ban: ban['created'], reverse=True)
        return render(request, 'minecraft_manager/ban.html', {'current_app': 'ban', 'bans': bans})


class Alert(View):

    def get(self, request):
        alerts = AlertModel.objects.filter(user=request.user).order_by('seen', '-id')
        unseen_alerts = AlertModel.objects.filter(user=request.user, seen=False)
        return render(request, 'minecraft_manager/alert.html',
                                  {'current_app': 'alert', 'alerts': alerts, 'unseen': len(unseen_alerts)})

    def post(self, request):
        post = request.POST
        if 'action' in post:
            if post['action'] == 'mar':
                AlertModel.objects.filter(user=request.user).update(seen=True)
        alerts = AlertModel.objects.filter(user=request.user).order_by('seen', '-id')
        return render(request, 'minecraft_manager/alert.html',
                                  {'current_app': 'alert', 'alerts': alerts, 'unseen': 0})


class AlertInfo(View):

    def get(self, request, alert_id):
        alert = AlertModel.objects.get(id=alert_id)
        alert.seen = True
        alert.save()
        return render(request, 'minecraft_manager/alert_info.html',
                                  {'current_app': 'alert', 'alert': alert})

    def post(self, request, alert_id):
        post = request.POST
        alert = AlertModel.objects.get(id=alert_id)
        if 'action' in post:
            if post['action'] == 'mu':
                alert.seen = False
                alert.save()
        return render(request, 'minecraft_manager/alert_info.html',
                                  {'current_app': 'alert', 'alert': alert})


class Application(View):

    def get(self, request):
        get = request.GET
        if 'accepted' in get:
            if get['accepted'].lower() == 'true':
                applications = AppModel.objects.filter(accepted=True)
            elif get['accepted'].lower() == 'false':
                applications = AppModel.objects.filter(accepted=False)
            else:
                applications = AppModel.objects.filter(accepted__isnull=True)
        else:
            applications1 = AppModel.objects.filter(accepted__isnull=True)
            applications2 = AppModel.objects.filter(accepted__isnull=False)
            applications = list(chain(applications1, applications2))
        return render(request, 'minecraft_manager/application.html', {'current_app': 'application', 'applications': applications})


class Reference(View):

    def get(self, request):
        get = request.GET
        applications = AppModel.objects.all()
        return render(request, 'minecraft_manager/reference.html', {'current_app': 'application', 'applications': applications})


class ApplicationInfo(View):

    @method_decorator(csrf_protect)
    def get(self, request, application_id):
        application = AppModel.objects.get(id=application_id)
        return render(request, 'minecraft_manager/application_info.html', {'current_app': 'application', 'application': application})

    def post(self, request, application_id):
        post = request.POST
        application = AppModel.objects.get(id=application_id)
        if post['accept']:
            if post['accept'] == 'accept':
                application.accepted = True
            if post['accept'] == 'deny':
                application.accepted = False
            application.save()
            API.plugin(post['accept'], application.username)
            API.discord_mcm("Application #**{0}** was **{1}** by **{2}**".format(application.id, "Accepted" if application.accepted else "Denied", request.user.player.username))
        return render(request, 'minecraft_manager/application_info.html',
                                  {'current_app': 'application', 'application': application})


class Player(View):

    def get(self, request):
        get = request.GET
        if 'search' in get:
            search = get['search']
            player = resolve_player(search)
            if player:
                return redirect('{}{}/'.format(reverse('player'), player.id))
            else:
                players = PlayerModel.objects.filter(username__icontains=search)
        else:
            players = PlayerModel.objects.all()
        ban_file = os.path.join(settings.MINECRAFT_BASE_DIR, 'banned-players.json')
        with open(ban_file, encoding='utf-8') as f:
            ban_list = json.load(f)
        bans = []
        for ban in ban_list:
            bans.append(ban['uuid'])
        return render(request, 'minecraft_manager/player.html', {'current_app': 'player', 'players': players, 'bans': bans})


class PlayerInfo(View):

    def get(self, request, player_id):
        player = PlayerModel.objects.get(id=player_id)
        ips = IPModel.api.filter(player=player)
        tickets = TicketModel.objects.filter(player=player)
        notes = NoteModel.objects.filter(player=player)
        form = {'ips': ips, 'tickets': tickets, 'notes': notes}
        return render(request, 'minecraft_manager/player_info.html',
                      {'current_app': 'player', 'player': player, 'form': form})

    def post(self, request, player_id):
        player = PlayerModel.objects.get(id=player_id)
        ips = IPModel.api.filter(player=player)
        tickets = TicketModel.objects.filter(player=player)
        notes = NoteModel.objects.filter(player=player)
        form = {'ips': ips, 'tickets': tickets, 'notes': notes}
        return render(request, 'minecraft_manager/player_info.html',
                      {'current_app': 'player', 'player': player, 'form': form})


class Ticket(View):

    def get(self, request):
        get = request.GET
        tickets1 = TicketModel.objects.filter(resolved=False).order_by('-id')
        tickets2 = TicketModel.objects.filter(resolved=True).order_by('-id')
        tickets = list(chain(tickets1, tickets2))
        return render(request, 'minecraft_manager/ticket.html', {'current_app': 'ticket', 'tickets': tickets})


class TicketInfo(View):

    @method_decorator(csrf_protect)
    def get(self, request, ticket_id):
        ticket = TicketModel.objects.get(id=ticket_id)
        active_staff = User.objects.filter(is_active=True)
        inactive_staff = User.objects.filter(is_active=False)
        ticket_notes = TicketNoteModel.objects.filter(ticket=ticket)
        ticket_note_form = TicketNoteForm(instance=request.user)
        has_ticket_note = False
        for ticket_note in ticket_notes:
            if ticket_note.author == request.user:
                ticket_note_form = TicketNoteForm(instance=ticket_note)
                has_ticket_note = True
        if not has_ticket_note:
            ticket_note_form.fields['ticket'].initial = ticket_id
        form = {'active_staff': active_staff, 'inactive_staff': inactive_staff, 'priority': TicketModel.PRIORITY,
                'resolved': ticket.resolved, 'ticket_note_form': ticket_note_form.as_p(), 'ticket_notes': ticket_notes, 'has_ticket_note': has_ticket_note,
                'show_ticket_note': False}
        return render(request, 'minecraft_manager/ticket_info.html', {'current_app': 'ticket', 'ticket': ticket, 'form': form})

    def post(self, request, ticket_id):
        post = request.POST
        ticket = TicketModel.objects.get(id=ticket_id)
        if 'priority' in post:
            if post['priority'] != ticket.priority:
                API.discord_mcm(
                    "Ticket #**{0}**'s priority was changed from **{1}** to **{2}** by **{3}**".format(ticket.id,
                                                                                               ticket.priority_display,
                                                                                               TicketModel.priority_code_to_display(
                                                                                                   post['priority']),
                                                                                               request.user.username))
            ticket.priority = post['priority']
        if 'staff' in post and 'resolved' not in post:
            if not ticket.staff or request.user.is_staff:
                staff = User.objects.get(id=post['staff'])
                if post['staff'] != str(getattr(ticket.staff, 'id', '-1')):
                    if post['staff'] == str(request.user.id):
                        API.discord_mcm(
                            "Ticket #**{0}** was claimed by **{1}**".format(ticket.id, request.user.username))
                    else:
                        API.discord_mcm(
                            "Ticket #**{0}** was given to **{1}** by **{2}**".format(ticket.id, staff.username, request.user.username))
                ticket.staff = staff
        if 'resolved' in post:
            API.discord_mcm("Ticket #**{0}** was resolved by **{1}**".format(ticket.id, request.user.username))
            ticket.resolved = True
        ticket.save()

        show_ticket_note = False
        if 'ticket_note' in post:
            ticket_note_form = TicketNoteForm(post)
            if ticket_note_form.is_valid():
                n = ticket_note_form.save(commit=False)
                if post['ticket_note'] == 'create':
                    n.author = request.user
                    n.save()
                elif post['ticket_note'] == 'edit':
                    db = TicketNoteModel.objects.get(ticket=ticket, author=request.user)
                    db.message = n.message
                    db.last_update = timezone.now()
                    db.save()
            else:
                show_ticket_note = True
        else:
            ticket_note_form = TicketNoteForm(instance=request.user)
        ticket_notes = TicketNoteModel.objects.filter(ticket=ticket)
        has_ticket_note = False
        for ticket_note in ticket_notes:
            if ticket_note.author == request.user:
                ticket_note_form = TicketNoteForm(instance=ticket_note)
                has_ticket_note = True
        if not has_ticket_note:
            ticket_note_form.fields['ticket'].initial = ticket_id
        active_staff = User.objects.filter(is_active=True)
        inactive_staff = User.objects.filter(is_active=False)
        form = {'active_staff': active_staff, 'inactive_staff': inactive_staff, 'priority': TicketModel.PRIORITY,
                'resolved': ticket.resolved, 'ticket_note_form': ticket_note_form.as_p(), 'ticket_notes': ticket_notes, 'has_ticket_note': has_ticket_note,
                'show_ticket_note': show_ticket_note}
        return render(request, 'minecraft_manager/ticket_info.html',
                                  {'current_app': 'ticket', 'ticket': ticket, 'form': form})


class Note(View):

    def get(self, request):
        notes = NoteModel.objects.order_by('-id')
        return render(request, 'minecraft_manager/note.html', {'current_app': 'note', 'notes': notes})


class NoteInfo(View):

    @method_decorator(csrf_protect)
    def get(self, request, note_id):
        note = NoteModel.objects.get(id=note_id)
        form = {'importance': NoteModel.IMPORTANCE}
        return render(request, 'minecraft_manager/note_info.html', {'current_app': 'note', 'form': form, 'note': note})

    def post(self, request, note_id):
        post = request.POST
        note = NoteModel.objects.get(id=note_id)
        if 'importance' in post:
            API.discord_mcm("Note #**{0}**'s importance was changed from {1} to {2} by {3}".format(note.id,
                                                                                                             note.importance_display,
                                                                                                             NoteModel.importance_code_to_display(
                                                                                                                 post[
                                                                                                                     'importance']),
                                                                                                             request.user.player.username))
            note.importance = post['importance']
        note.save()
        form = {'importance': NoteModel.IMPORTANCE}
        return render(request, 'minecraft_manager/note_info.html',
                                  {'current_app': 'note', 'form': form, 'note': note})


class NoteAdd(View):

    @method_decorator(csrf_protect)
    def get(self, request):
        get = request.GET
        form = NoteForm()
        if 'player' in get:
            form.initial = {'player': get['player']}
        return render(request, 'minecraft_manager/note_add.html',
                                  {'current_app': 'note', 'form': form.as_p()})

    def post(self, request):
        post = request.POST
        form = NoteForm(post)
        if form.is_valid():
            note = form.save()
            note.staff = request.user
            note.save()
            API.discord_mcm(
                "**{0}** made a **{1}** importance note for **{2}**\nPreview: {3}".format(note.staff.player.username,
                                                                                            note.importance_display,
                                                                                            note.player.username,
                                                                                            note.snippet))
            return redirect("{0}{1}".format(reverse('note'), note.id))
        else:
            return render(request, 'minecraft_manager/note_add.html',
                                      {'current_app': 'note', 'form': form})


class IP(View):

    def get(self, request, ip_id):
        ip = IPModel.api.get(id=ip_id)
        ips = IPModel.api.filter(ip=ip.ip)
        return render(request, 'minecraft_manager/ip.html', {'ip': ip, 'ips': ips})

    def post(self, request, ip_id):
        pass


class Report(View):

    def get(self, request):
        get = request.GET
        results = []
        timestamp = None
        report = get.get('report')
        world = get.get('world')
        if report and world:
            # Get some results
            report_dir = os.path.join(settings.MINECRAFT_BASE_DIR, 'plugins', 'MinecraftManager', 'report.json')
            if os.path.exists(report_dir):
                with open(report_dir) as rpt:
                    raw = json.load(rpt)
                time = raw.get('time')
                timestamp = datetime.datetime.utcfromtimestamp(float(time))
                timestamp = pytz.timezone('UTC').localize(timestamp)
                timestamp = timestamp.astimezone(pytz.timezone(request.user.usersettings.default_timezone))
                for w in raw:
                    if w.endswith('_nether') and world == 'nether':
                        world_raw = raw[w]
                    elif w.endswith('_the_end') and world == 'end':
                        world_raw = raw[w]
                    elif w != 'time' and not (w.endswith('_nether') or w.endswith('_the_end')) and world == 'overworld': # Need to think of a way to clean this up...
                        world_raw = raw[w]
                        break
                results_raw = world_raw.get(report)
                for result in results_raw:
                    if report == 'players':
                        results.append({'name': result.get('name'), 'x': result.get('x'), 'y': result.get('y'),
                                        'z': result.get('z')})
                    else:
                        type_formatted = ' '.join([part.title() for part in result.split('_')])
                        if report == 'entities':
                            for location in results_raw[result]:
                                results.append({'name': type_formatted, 'x': location.get('x'), 'y': location.get('y'),
                                                'z': location.get('z')})
                        else:
                            results.append({'name': type_formatted, 'count': results_raw.get(result)})

            else:
                results = 'NONE'
        elif report or world:
            results = 'BOTH'
        if len(results) == 0:
            results = 'EMPTY'
        return render(request, 'minecraft_manager/report.html', {'current_app': 'report', 'report': report, 'world': world, 'timestamp': timestamp, 'results': results})

    def post(self, request):
        pass


class Chat(View):


    @staticmethod
    def replace_ascii(message):
        return message.replace(" ", "\\040").replace("\"", "\\042").replace("#", "\\043").replace("$", "\\044")\
            .replace("%", "\\045").replace("&", "\\046").replace("(", "\\050").replace(")", "\\051").replace("*", "\\052")\
            .replace("+", "\\053").replace("-", "\\055")

    def get(self, request):
        staff = hasattr(settings, 'STAFF_LOG')
        return render(request, 'minecraft_manager/chat.html', {'current_app': 'chat', 'staff': staff})

    def post(self, request):
        post = request.POST
        if 'chat' in post and 'message' in post:
            API.plugin(post['chat'], "{0} {1}".format(request.user.player.username, post['message']))
            data = {'success': True, 'message': 'Message sent successfully.'}
        else:
            data = {'success': False, 'message': 'No chat type or message set.'}
        return JsonResponse(data)


class Bots(View):

    def get_bots(self):
        bot_dir = getattr(settings, 'BOT_DIR', None)
        bots = []
        if bot_dir:
            for file in os.listdir(bot_dir):
                if file.endswith('.bot.py'):
                    ve = file.replace('.bot.py', '')
                    py = os.path.join(bot_dir, ve, 'bin/python')
                    if os.path.isfile(py):
                        bots.append(Bot(file.replace('.bot.py', ''), False, py))
                    else:
                        bots.append(Bot(file.replace('.bot.py', ''), False, sys.executable))
        # Also get packaged MCM bots
        if getattr(settings, 'DISCORD_BOT_TOKEN', None):
            bots.append(Bot("Discord-MCM", True, None, discord_start, discord_stop, discord_restart, discord_status, discord_display))
        return bots

    def get(self, request):
        return render(request, 'minecraft_manager/bots.html', {'current_app': 'bots', 'bots': self.get_bots()})

    def post(self, request):
        post = request.POST
        for bot in self.get_bots():
            if bot.name in post:
                if post[bot.name] == "stop":
                    bot.stop()
                elif post[bot.name] == "start":
                    bot.start()
                elif post[bot.name] == "restart":
                    bot.restart()
        return render(request, 'minecraft_manager/bots.html', {'current_app': 'bots', 'bots': self.get_bots()})
