import json

from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin
from django.db.models import Q
from django.shortcuts import render, HttpResponse
from django.http import HttpResponseBadRequest, JsonResponse
from django.views.generic import TemplateView
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt

from bloonsa_game.models import Level, LevelRating, LevelScore
from users.models import Player
from users.util import bloonsa_util, actions


class CSRFexemptTemplateView(TemplateView):
    @method_decorator(csrf_exempt)
    def dispatch(self, *args, **kwargs):
        return super().dispatch(*args, **kwargs)


class LoadLevel(CSRFexemptTemplateView):
    def post(self, request, *args, **kwargs):
        level_id = request.POST.get("level_id")
        if level_id is None or not level_id.isdigit():
            return HttpResponseBadRequest()

        level: Level = Level.objects.filter(level_id=int(level_id)).first()
        if level is None:
            return HttpResponseBadRequest()

        if request.user.is_authenticated:
            player: Player = request.user.player
            player.bloonsa_levels_played.add(level)
            bloonsa_util.log(player=player,
                             action=actions.bloonsa_load_level_by_id,
                             note=level.level_id)

        flashVars = level.get_flash_vars(seperator="&")
        return HttpResponse(flashVars)


class RandomLevel(CSRFexemptTemplateView):
    def post(self, request, *args, **kwargs):
        if request.user.is_authenticated:
            player: Player = request.user.player

            # Make this filter only beaten_by
            level: Level = Level.objects.order_by("?").filter(~Q(played_by=player)).first()
            if not level:
                level: Level = Level.objects.order_by("?").first()

            if not player.bloonsa_levels_played.filter(pk=level.pk).exists():
                level.played_by.add(player)
                level.save()
            bloonsa_util.log(player=player,
                             action=actions.bloonsa_load_random_level,
                             note=level.level_id)
        else:
            level: Level = Level.objects.order_by("?").first()

        flashVars = level.get_flash_vars(seperator="&")
        return HttpResponse(flashVars)

    def get(self, request, *args, **kwargs):
        return self.post(request=request, *args, **kwargs)


class CompleteLevel(CSRFexemptTemplateView):

    def post(self, request, *args, **kwargs):
        if not request.user.is_authenticated:
            return HttpResponse(content="GG", status=200)

        level_id = int(request.POST.get("level_id"))
        darts_left = int(request.POST.get("darts_left"))
        dart_glitch = bool(request.POST.get("dart_glitch") == "true")
        pops = int(request.POST.get("pops"))

        level: Level = Level.objects.get(level_id=level_id)
        player: Player = request.user.player

        score = LevelScore.objects.filter(player=player,
                                          level=level).first()
        clear = pops >= level.target

        if score is None:
            score = LevelScore.objects.create(level=level,
                                              clear=clear,
                                              darts_left=darts_left,
                                              dart_glitch=dart_glitch,
                                              dart_glitch_ever=dart_glitch,
                                              pops=pops)
            player.bloonsa_level_scores.add(score)
            score.save()
            player.save()

        # Check if score is an improvement
        elif any((
            pops > score.pops,
            pops == score.pops and darts_left + int(dart_glitch) > score.darts_left,
            clear and not score.clear
        )):
            # Has dart glitch ever been done in a score?
            dart_glitch_ever = bool(dart_glitch or score.dart_glitch_ever or score.dart_glitch)

            score.update(commit=True,
                         clear=clear,
                         darts_left=darts_left,
                         dart_glitch=dart_glitch,
                         dart_glitch_ever=dart_glitch_ever,
                         pops=pops)

        # Check if we just did a dart glitch for fun
        elif dart_glitch:
            score.update(commit=True,
                         dart_glitch_ever=True)

        bloonsa_util.log(player=player,
                         action=actions.bloonsa_submit_score,
                         note=level.level_id)

        # Sending empty content means error in as3
        return HttpResponse(content="GG", status=200)

class RateLevel(CSRFexemptTemplateView):

    def post(self, request, *args, **kwargs):
        if not request.user.is_authenticated:
            return HttpResponse(content="OK", status=200)

        rating = int(request.POST.get("rating"))
        level_id = int(request.POST.get("level_id"))
        level: Level = Level.objects.get(level_id=level_id)
        player: Player = request.user.player
        ratingObject = player.bloonsa_level_ratings.filter(level=level).first()
        bloonsa_util.log(player=player,
                         action=actions.bloonsa_rate_level,
                         note=level.level_id)
        if ratingObject:
            ratingObject.rating = rating
            ratingObject.save()
            return HttpResponse(content="OK", status=200)

        rating = LevelRating.objects.create(level=level,
                                            rating=rating)
        player.bloonsa_level_ratings.add(rating)
        rating.save()
        player.save()
        return HttpResponse(content="OK", status=200)


class GetStatusData(CSRFexemptTemplateView):

    def post(self, request, *args, **kwargs):
        level_id = int(json.loads(request.body).get("level_id"))

        level: Level = Level.objects.get(level_id=level_id)
        total_levels = Level.objects.count()
        level_plays = level.play_count
        level_wins = level.win_count
        level_win_percentage = 0.00 if level_wins == 0 else level_wins / level_plays
        dart_glitch_count = level.dart_glitch_count

        jdata = {
            "level_author_name": level.author.name,
            "level_author_id": level.author.id,
            "level_title": level.title,
            "level_id": level.level_id,
            "level_legacy_plays": level.plays,
            "level_legacy_rating": level.rating,
            "level_darts": level.darts,
            "level_target": level.target,
            "level_rating": level.stars,
            "level_plays": level_plays,
            "level_wins": level_wins,
            "level_win_percentage": level_win_percentage,
            "dart_glitch_count": dart_glitch_count,
        }

        if request.user.is_authenticated:
            player: Player = request.user.player
            score: LevelScore = LevelScore.objects.filter(player=player,
                                                          level=level).first()
            level_cleared = player.has_beaten_bloonsa_level(level=level)

            jdata.update({
                "bloonsa_levels_played": player.bloonsa_levels_played_count,
                "bloonsa_levels_beaten": player.bloonsa_levels_beaten_count,
                "bloonsa_total_levels": total_levels,
                "level_cleared": level_cleared,
            })
            if score:
                jdata.update({
                    "score_darts_left": score.darts_left,
                    "score_dart_glitch": score.dart_glitch,
                    "score_dart_glitch_ever": score.dart_glitch_ever,
                    "score_pops": score.pops,
                })

        return JsonResponse(jdata)