import json

from django.db.models import Avg, F, Sum, Case, When, IntegerField, Q, FloatField
from django.utils.dateparse import parse_datetime
from django.http import HttpResponse, JsonResponse
import random
from .models import *
from .serializers import *
from rest_framework import viewsets
from rest_framework.decorators import action, api_view
from rest_framework.response import Response
from datetime import datetime, timezone


def get_gamerun_by_id(id):
    return GameRuns.objects.get(gamerun_id=id)


# TODO: set number of emails and % of phishing emails
def set_random_emails(gamerun_id):
    number = Settings.objects.get(item__exact="Number of emails").value
    perc_of_phishing = Settings.objects.get(item__exact="Phishing emails").value

    number_of_legitimate = int(round(number * (perc_of_phishing / 100), 0))
    number_of_phishing = number - number_of_legitimate

    phishing_emails = list(Emails.objects.filter(type__type_id__gt=1))
    legitimate_emails = list(Emails.objects.filter(type__type_id=1))

    r_phishing_emails = random.sample(phishing_emails, number_of_phishing)
    r_legitimate_emails = random.sample(legitimate_emails, number_of_legitimate)

    GameRuns.objects.get(gamerun_id=gamerun_id).emails.set(r_phishing_emails + r_legitimate_emails)


@api_view(('GET',))
def next_email(request):
    gamerun = get_gamerun_by_id(request.session['gamerun'])

    if request.session['remaining'] <= 0:  # TODO zmenit na file nastavenia
        return Response({"end": True})

    email = gamerun.emails.filter(emailgameruns__player_answer__isnull=True).first()

    request.session['current_email'] = email.email_id
    request.session['start_time'] = datetime.now(timezone.utc).__str__()
    request.session['remaining'] -= 1

    return JsonResponse({"type": email.type.type, "subject": email.subject, "sender": email.sender, "body": email.body})


# TODO: make session expire on page reload
@api_view(('POST',))
def init_gamerun(request):
    username = request.data['username']
    gamerun = GameRuns.objects.create()

    request.session['username'] = username
    request.session['gamerun'] = gamerun.gamerun_id
    request.session['current_email'] = 0
    request.session['remaining'] = Settings.objects.get(item__exact="Number of emails").value

    set_random_emails(gamerun.gamerun_id)
    return Response("successful")


# TODO : repair update
@api_view(('POST',))
def set_player_answer(request):
    gamerun_id = request.session['gamerun']
    email_id = request.session['current_email']

    egrun = EmailGameRuns.objects.get(email__exact=email_id, game_run=gamerun_id)
    egrun.player_answer = request.data['answer']
    duration = datetime.now(timezone.utc) - parse_datetime(request.session['start_time'])
    egrun.duration = duration

    egrun.save()

    return Response("successful")


def count_average_duration(email: Emails):
    egruns = EmailGameRuns.objects.filter(email_id=email.email_id)
    average_duration = egruns.aggregate(average_duration=Avg(F('duration'))).get('average_duration')

    return str(average_duration).split('.')[0]


def count_average_correctness(email: Emails):
    egruns = EmailGameRuns.objects.filter(email_id=email.email_id)

    average_correctness = egruns.aggregate(average_correctness=Avg(Case(
        When((Q(player_answer=True) & ~Q(email__type__type__exact="legitimate"))
             | (Q(player_answer=False) & Q(email__type__type__exact="legitimate")), then=100.0), default=0.0),
        output_field=FloatField())).get('average_correctness')

    return str(average_correctness).split('.')[0]


def get_players_answer_duration(gamerun: GameRuns, email: Emails):
    duration = EmailGameRuns.objects.get(Q(email_id=email.email_id) & Q(game_run_id=gamerun.gamerun_id)).duration

    return str(duration).split('.')[0]


def get_email_occurrence_number(email: Emails):
    occurrence = EmailGameRuns.objects.filter(email_id=email.email_id).count()

    return occurrence


def check_answer_correctness(gamerun: GameRuns, email: Emails):
    egrun = EmailGameRuns.objects.get(Q(email_id=email.email_id) & Q(game_run_id=gamerun.gamerun_id))
    answer_correctness = False

    if not egrun.player_answer & (egrun.email.type.type == "legitimate"):
        answer_correctness = True
    elif egrun.player_answer & (egrun.email.type.type != "legitimate"):
        answer_correctness = True

    return {'player_answer': egrun.player_answer, 'correctness': answer_correctness}


@api_view(('GET',))
def get_gamerun_summary(request):
    gamerun_id = request.session['gamerun']
    gamerun = get_gamerun_by_id(gamerun_id)
    statistics = []
    emails = GameRuns.objects.get(gamerun_id=gamerun.gamerun_id).emails
    counter_correct_answers = 0

    for email in emails.iterator():
        average_correctness = count_average_correctness(email)
        average_duration = count_average_duration(email)
        players_duration = get_players_answer_duration(gamerun, email)
        answer = check_answer_correctness(gamerun, email)
        counter_correct_answers += 1 if (answer.get('correctness')) else 0
        statistics.append({'email': EmailSerializer(email).data, 'average_correctness': average_correctness,
                           'average_duration': average_duration, 'players_duration': players_duration,
                           'answer': answer
                           })

    return Response(statistics)


@api_view(('POST',))
def get_email_statistics(email_id):
    email = Emails.objects.get(email_id=email_id)

    occurrence = get_email_occurrence_number(email)
    average_duration = count_average_duration(email)
    average_correctness = count_average_correctness(email)

    return Response({'Times in test': occurrence, 'Average duration': average_duration,
                     'Average correctness': average_correctness})


class EmailViewSet(viewsets.ModelViewSet):
    queryset = Emails.objects.all()
    serializer_class = EmailSerializer


class SignViewSet(viewsets.ModelViewSet):
    queryset = Signs.objects.all()
    serializer_class = SignSerializer


class TypeViewSet(viewsets.ModelViewSet):
    queryset = Types.objects.all()
    serializer_class = TypeSerializer


class GameRunViewSet(viewsets.ModelViewSet):
    queryset = GameRuns.objects.all()
    serializer_class = GameRunSerializer


class EmailGameRunViewSet(viewsets.ModelViewSet):
    queryset = EmailGameRuns.objects.all()
    serializer_class = EmailGameRunSerializer


class SettingViewSet(viewsets.ModelViewSet):
    queryset = Settings.objects.all()
    serializer_class = SettingSerializer
