from __future__ import absolute_import

import logging, datetime
from django.contrib.auth.forms import PasswordChangeForm
from django.contrib.auth import update_session_auth_hash
from django.apps import apps
from django.conf import settings
from django.contrib.auth.models import User
from django.http import JsonResponse, HttpResponse
from django.utils import timezone
from django.views.generic import View
from django.forms import modelform_factory

import minecraft_manager.forms as mcm_forms
from minecraft_manager.models import Player, UserSettings, Application, IP, Ticket, Note
import minecraft_manager.api.api as mcm_api
from minecraft_manager.api.models import Token
import minecraft_manager.utils as mcm_utils
import minecraft_manager.external.stats as mcm_stats

logger = logging.getLogger(__name__)


def request_allowed(request, permission):
    is_authenticated = False
    if hasattr(request, 'user'):
        if hasattr(request.user, 'is_authenticated'):
            is_authenticated = request.user.is_authenticated
    get = request.GET
    post = request.POST
    request_password = None
    if 'api' in get:
        request_password = get['api']
    elif 'api' in post:
        request_password = post['api']
    token_permission = False
    if Token.objects.filter(active=True, key=request_password).exists():
        token = Token.objects.get(active=True, key=request_password)
        token_permission = getattr(token, permission, False)
    return is_authenticated or token_permission


def clean(model, data):
    cleaned = {}
    for d in data:
        attr = d
        if '__' in d:
            attr = d.split('__')[0]
        if hasattr(model, attr):
            cleaned[d] = data[d]
    return cleaned


class WebAPI(View):

    def get(self, request, keyword):
        get = request.GET
        data = {'success': False, 'message': 'API failed'}
        if request_allowed(request, 'web_get_permission'):
            keyword = keyword.lower()
            if keyword == 'log':
                html_global = ""
                html_staff = ""
                chats = mcm_api.get_chats(request.user.usersettings.default_timezone)
                if chats:
                    for g in chats['global']:
                        g['text'] = g['text'].replace("<", "&lt;").replace(">", "&gt;")
                        if request.user.usersettings.show_timestamp_chat:
                            html_global += "<div>[{0}] {1}</div>".format(g['date'], g['text'])
                        else:
                            html_global += "<div data-toggle='tooltip' title='{0}' data-placement='left'>{1}</div>".format(g['date'], g['text'])
                    for s in chats['staff']:
                        s['text'] = s['text'].replace("<", "&lt;").replace(">", "&gt;")
                        if request.user.usersettings.show_timestamp_chat:
                            html_staff += "<div>[{0}] {1}</div>".format(s['date'], s['text'])
                        else:
                            html_staff += "<div data-toggle='tooltip' title='{0}' data-placement='left'>{1}</div>".format(s['date'], s['text'])
                html = {'global': html_global, 'staff': html_staff}
                data = {'chats': chats, 'html': html}
            elif keyword == 'online':
                query = mcm_api.get_query()
                html = ""
                for p in query['players']:
                    html += "<div class='label label-primary'>{0}</div>".format(p)
                    html += "<br/>"
                data = {'query': query, 'html': html}
            elif keyword == "coreprotect":
                if 'username' in get and 'ip' in get:
                    user = User.objects.get(username=get['username'])
                    if user.is_active and user.usersettings.last_ip == get['ip']:
                        data = {'success': True, 'message': self.access_level(user)}
                else:
                    data = {'success': False, 'message': 'Parameters not set'}
        else:
            data = {'message': 'Not Authorized', 'success': False}
        return JsonResponse(data)

    def post(self, request, keyword):
        post = request.POST
        data = {}
        if request_allowed(request, 'web_post_permission'):
            keyword = keyword.lower()
            if keyword == 'settings' and request.user.usersettings:
                for s in [a for a in dir(UserSettings) if not a.startswith('__') and not callable(getattr(UserSettings,a))]:
                    if s in post:
                        setattr(request.user.usersettings, s, post[s])
                request.user.usersettings.save()
                data = {'success': True, 'message': 'User Settings saved'}
            elif keyword == 'password':
                form = PasswordChangeForm(request.user, post)
                if form.is_valid():
                    user = form.save()
                    update_session_auth_hash(request, user)
                    return HttpResponse("success")
                else:
                    return HttpResponse(form.as_p())
            elif keyword == 'alert':
                form = mcm_forms.AlertForm(request.POST)
                if form.is_valid():
                    if mcm_api.create_alert(form.cleaned_data['message']):
                        data = {'success': True}
                    else:
                        data = {'success': False, 'message': 'Could not create Alerts'}
                else:
                    data = {'success': False, 'message': 'Invalid Message'}
            elif keyword == "discord":
                if 'message' in post:
                    ping = post['ping'] if 'ping' in post else False
                    mcm_api.discord_notification(post['message'], ping=ping)
                    data = {'success': True, 'message': "Success", 'extra': {'message': post['message'], 'ping': ping}}
                else:
                    data = {'success': False, 'message': 'No message supplied'}
            else:
                data = {'success': True, 'message': 'Model set to "{0}"'.format(keyword)}
        else:
            data = {'message': 'Not Authorized', 'success': False}
        return JsonResponse(data)

    def access_level(self, user):
        access = {'cpp': False, 'cpf': False, 'cpa': False}
        if user.has_perm('minecraft_manager.coreprotect_partial'):
            access['cpp'] = True
        if user.has_perm('minecraft_manager.coreprotect_full'):
            access['cpf'] = True
        if user.has_perm('minecraft_manager.coreprotect_activity'):
            access['cpa'] = True
        return access


class PluginAPI(View):

    def get(self, request, keyword):
        json = {'status': True, 'message': '', 'extra': ''}
        if request_allowed(request, 'plugin_get_permission'):
            get = request.GET
            keyword = keyword.lower()

        return JsonResponse(json)

    def post(self, request, keyword):
        json = {'status': True, 'message': '', 'extra': ''}
        if request_allowed(request, 'plugin_post_permission'):
            post = request.POST
            keyword = keyword.lower()
            if "application" == keyword:
                if Application.objects.filter(username=post['username']).exists():
                    application = Application.objects.get(username=post['username'])
                    if application.accepted is not None:
                        json['status'] = False
                        json['message'] = "An application for {0} has already been acted on. Please contact Staff.".format(
                            post['username'])
                        return JsonResponse(json)
                    else:
                        application.accepted = None
                        application.age = post['age']
                        application.player_type = post['player_type']
                        application.ever_banned = False if post['ever_banned'] == "no" else True
                        application.ever_banned_explanation = post['ever_banned_explanation']
                        application.reference = post['reference']
                        application.read_rules = post['read_rules']
                else:
                    application = Application(username=post['username'], age=post['age'],
                                              player_type=post['player_type'],
                                              ever_banned=False if post['ever_banned'] == "no" else True,
                                              ever_banned_explanation= post['ever_banned_explanation'],
                                              reference=post['reference'], read_rules=post['read_rules'])
                application.save()
                if Player.objects.filter(username__iexact=post['username']).exists():
                    player = Player.objects.get(username__iexact=post['username'])
                    player.application_id = application.id
                    player.save()
                json['message'] = "{0}'s application was submitted.".format(application.username)
                json['extra'] = application.id
                msg = mcm_utils.build_application(application)
                mcm_api.discord_mcm(message='New Application!', embed=msg)
            elif "application_action" == keyword:
                if Application.objects.filter(id=post['application_id']).exists():
                    application = Application.objects.get(id=post['application_id'])
                    if application.accepted is not None:
                        json['status'] = False
                        json['message'] = "Application was already {0}.".format(
                            "accepted" if post['action'] == "True" else "denied")
                    else:
                        application.accepted = True if post['action'] == "True" else False
                        application.save()
                        json['message'] = "Application was successfully {0}.".format(
                            "accepted" if post['action'] == "True" else "denied")
                        mcm_api.discord_mcm("{0}'s application (#{1}) was {2} by {3}".format(application.username,
                                                                                               application.id,
                                                                                               "accepted" if post['action'] == "True" else "denied",
                                                                                                     post['username']))
                        mcm_api.plugin("accept" if post['action'] == "True" else "deny", application.username)
                else:
                    json['status'] = False
                    json['message'] = "No application found."
                    return JsonResponse(json)
            elif "application_clear" == keyword:
                if Application.objects.filter(id=post['application_id']).exists():
                    application = Application.objects.get(id=post['application_id'])
                    if application.accepted is True:
                        json['status'] = False
                        json['message'] = "An accepted application can't be cleared."
                    else:
                        application.accepted = None
                        application.save()
                        json['message'] = "Application was successfully cleared."
                else:
                    json['status'] = False
                    json['message'] = "No application found."
                    return JsonResponse(json)
            elif "login" == keyword:
                player = Player.objects.filter(uuid=post['uuid']).exists()
                new_player = False
                if not player:
                    player = Player(uuid=post['uuid'], username=post['username'])
                    new_player = True
                    player.first_seen = timezone.now().strftime("%Y-%m-%d")
                    test_app = Application.objects.filter(username__iexact=post['username']).exists()
                    if test_app:
                        test_app = Application.objects.get(username__iexact=post['username'])
                        player.application = test_app
                    player.save()
                else:
                    player = Player.objects.get(uuid=post['uuid'])
                    if player.username != post['username']:
                        user = User.objects.filter(username__iexact=player.username).exists()
                        if user:
                            user = User.objects.get(username__iexact=player.username)
                            user.username = post['username'].lower()
                            user.save()
                        mcm_api.create_alert("Name Change: {0} to {1}.".format(player.username, post['username']))
                        player.username = post['username']
                    if not player.application:
                        test_app = Application.objects.filter(username__iexact=post['username']).exists()
                        if test_app:
                            test_app = Application.objects.get(username__iexact=player.username)
                            player.application = test_app
                    player.save()
                test_ip = IP.objects.filter(ip=post['ip'], player=player).exists()
                last_used = datetime.date.today()
                if not test_ip:
                    ip = IP(ip=post['ip'], player=player)
                else:
                    ip = IP.objects.get(ip=post['ip'], player=player)
                ip.last_used = last_used
                ip.save()
                player = Player.objects.get(uuid=post['uuid'])
                player.last_seen = timezone.now().strftime("%Y-%m-%d")
                player.save()
                if new_player and ip.associated:
                    associated = []
                    for assoc in ip.associated:
                        if assoc.uuid is not player.uuid and assoc.is_banned:
                            associated.append(assoc)
                    if associated:
                        mcm_api.plugin("staff", "Server {0}'s IP matches the banned player(s) {1}".format(player.username, ", ".join([assoc.username for assoc in associated])))
                        mcm_api.discord_notification("{0}'s IP matches the banned player(s) {1}".format(player.username, ", ".join([assoc.username for assoc in associated])), ping=True)
                json['status'] = True
                json['message'] = "Updated {0}".format(post['username'])
            elif "register" == keyword:
                player = Player.objects.get(uuid=post['uuid'])
                if player.auth_user:
                    json['status'] = False
                    json['message'] = "You are already registered. To change your password, contact an Admin."
                else:
                    password = mcm_api.generate_password()
                    user = User.objects.create_user(username=player.username.lower(), password=password)
                    user.save()
                    player.auth_user = user
                    player.save()
                    json['message'] = password
            elif "ticket" == keyword:
                player = Player.objects.get(uuid=post['uuid'])
                try:
                    ticket = Ticket(player=player, message=post['message'], x=post['x'], y=post['y'], z=post['z'], world=post['world'])
                    ticket.save()
                    json['message'] = "Ticket submitted."
                    link = "{}".format(mcm_utils.url_path(settings.MCM_BASE_LINK, 'dashboard/ticket', ticket.id))
                    msg = mcm_utils.build_ticket(ticket, link)
                    json['extra'] = {'id': ticket.id, 'link': link}
                    mcm_api.discord_mcm(embed=msg, ping=True)
                except:
                    json['status'] = False
                    json['message'] = "Error while submitting ticket."
            elif "warning" == keyword:
                player = Player.objects.get(uuid=post['player'])
                staff = Player.objects.get(uuid=post['staff'])
                try:
                    warning = Note(player=player, message=post['message'], importance=post['severity'], staff=staff.auth_user)
                    warning.save()
                    json['message'] = "Warning issued."
                    link = "{}".format(mcm_utils.url_path(settings.MCM_BASE_LINK, 'dashboard/note', warning.id))
                    msg = mcm_utils.build_warning(warning, link)
                    mcm_api.discord_mcm(embed=msg)
                except Exception as ex:
                    json['status'] = False
                    json['message'] = "Error while issuing warning."
        return JsonResponse(json)


class FormAPI(View):

    def get(self, request, request_model):
        html = ""
        if request_allowed(request, 'form_get_permission'):
            get = request.GET
            model = None
            for m in apps.get_app_config('minecraft_manager').get_models():
                if m._meta.model_name.upper() == request_model.upper():
                    model = m
                    break
            if model:
                form = None
                for modelform in mcm_forms.__all__():
                    if modelform.Meta.model == model:
                        form = modelform()
                        break
                if not form:
                    form = modelform_factory(model, exclude=('id',))()
                if 'as' in get:
                    html = getattr(form, 'as_{0}'.format(get['as']))()
                else:
                    html = form.as_p()
        return HttpResponse(html)

    def post(self, request, request_model):
        html = ""
        if request_allowed(request, 'form_post_permission'):
            post = request.POST
            model = None
            for m in apps.get_app_config('minecraft_manager').get_models():
                if m._meta.model_name.upper() == request_model.upper():
                    model = m
                    break
            if model:
                form = None
                for modelform in mcm_forms.__all__():
                    if modelform.Meta.model == model:
                        form = modelform(post)
                        break
                if not form:
                    form = modelform_factory(model, exclude=('id',))(post)
                if form.is_valid():
                    form.save()
                    html = "Saved"
                else:
                    if 'as' in post:
                        html = getattr(form, 'as_{0}'.format(post['as']))()
                    else:
                        html = form.as_p()
        return HttpResponse(html)


class ModelAPI(View):

    def get(self, request, request_model):
        json = []
        if request_allowed(request, 'model_get_permission'):
            get = request.GET
            model = None
            for m in apps.get_app_config('minecraft_manager').get_models():
                if m._meta.model_name.upper() == request_model.upper():
                    model = m
                    break
            if model:
                keywords = clean(model, get)
                objects = model.api.filter(**keywords).values() if getattr(model, 'api', False) else model.objects.filter(**keywords).values()
                json = []
                for value in objects:
                    try:
                        link = "{}".format(mcm_utils.url_path(settings.MCM_BASE_LINK, 'dashboard', request_model, value['id']))
                        value['link'] = link
                    except:
                        pass
                    json.append(value)
        return JsonResponse(json, safe=False)

    def post(self, request, request_model):
        pass


class StatsAPI(View):

    def get(self, request):
        json = []
        if request_allowed(request, 'stats_get_permission'):
            get = request.GET
            if 'stat' in get:
                if 'uuid' in get:
                    json = mcm_stats.one_single(get['uuid'], get['stat'])
                elif 'username' in get and Player.objects.filter(username__iexact=get['username']).exists():
                    uuid = Player.objects.get(username__iexact=get['username']).uuid
                    json = mcm_stats.one_single(uuid, get['stat'])
                else:
                    json = mcm_stats.top_ten_stat(get['stat'])
            elif 'uuid' in get:
                json = mcm_stats.top_ten_player(get['uuid'])
            elif 'username' in get:
                uuid = Player.objects.get(username__iexact=get['username']).uuid
                json = mcm_stats.top_ten_player(uuid)
        return JsonResponse(json, safe=False)

    def post(self, request):
        pass
