import random
from datetime import datetime, timezone

from django.db.models import Q

# Create your views here.
from django.utils.dateparse import parse_datetime
from rest_framework import status
from rest_framework.decorators import api_view
from rest_framework.response import Response

from emails.models import Emails
from emails.serializers import EmailSerializer
from gameruns.models import GameRuns, EmailGameRuns
from settings.models import Settings


@api_view(('POST',))
def init_gamerun(request):
    """
    Creates gamerun object with given username and calls
    set_random_emails to assign emails to gamerun

    :param request: http request
    :returns: HTTP_201 if successful
    """
    username = request.data['username']

    gamerun = GameRuns.objects.create(player_name=username)

    request.session['gamerun'] = gamerun.id
    request.session['order'] = 0
    request.session['current_email'] = -1
    request.session['remaining'] = Settings.objects.get(
        key__exact="number_of_emails"
    ).value
    request.session.modified = True

    set_random_emails(gamerun.id)
    return Response("Successful", status=status.HTTP_201_CREATED)


@api_view(('POST',))
def set_player_answer(request):
    """
        Function sets player_answer and answer duration
        of current email in player's gamerun saved in session.

        :param request: http request
        :returns: HTTP_201 if successful
    """
    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']

    # calculate duration by subtracting datetime in session and actual datetime
    duration = datetime.now(timezone.utc) - parse_datetime(request.session['email_start_time'])
    egrun.duration = duration

    # decrement number of remaining emails in gamerun
    request.session['remaining'] -= 1
    egrun.save()

    return Response("Successful", status=status.HTTP_201_CREATED)


@api_view(('GET',))
def next_email(request):
    """
    Gets gamerun from session and return first email, which
    hasn't been answered by the player yet.

    :param request: http request
    :returns: unanswered email from current gamerun
    """
    gamerun_id = request.session['gamerun']  # get gamerun id from session
    gamerun = GameRuns.objects.filter(id__exact=gamerun_id).first()  # find player's gamerun

    # player has answered all emails
    if request.session['remaining'] <= 0:  # Checking if player answered all emails
        # set end time of gamerun to current datetime
        gamerun.end_time = datetime.now(tz=timezone.utc)
        gamerun.save()
        return Response({"end": True})

    email = gamerun.emails.filter(emailgameruns__player_answer__isnull=True).first()  # Get first unanswered email

    request.session['current_email'] = email.id
    request.session['email_start_time'] = datetime.now(timezone.utc).__str__()
    request.session.modified = True

    return Response(EmailSerializer(email).data)


def set_random_emails(gamerun_id):
    """
    Gets random legitimate and phishing emails
    and sets them to the given gamerun, without repetition

    :param gamerun_id: ID of gamerun
    :returns: None
    """

    # from settings get how many emails should be in gamerun
    number_of_emails = Settings.objects.get(key__exact="number_of_emails").value
    # from settings get ratio of phishing emails in gamerun
    perc_of_phishing = Settings.objects.get(key__exact="phishing_emails").value

    #
    number_of_phishing_emails = int(
        round(
            number_of_emails * (perc_of_phishing / 100), 0
        ))
    number_of_legit_emails = number_of_emails - number_of_phishing_emails

    # email with type id 1 is legitimate, type id > 1 means phishing
    phishing_emails = list(Emails.objects.filter(type__id__gt=1))  # phishing emails
    legitimate_emails = list(Emails.objects.filter(type__id=1)) # legitimate emails

    random_phishing_emails = random.sample(phishing_emails, number_of_phishing_emails)
    random_legitimate_emails = random.sample(legitimate_emails, number_of_legit_emails)

    GameRuns.objects.get(id=gamerun_id).emails.set(random_phishing_emails + random_legitimate_emails)


def check_answer_correctness(gamerun: GameRuns, email: Emails):
    """
        Gets player's answer of given **email** in given **gamerun**.

        :param gamerun: gamerun object
        :param email: email object
        :returns: player answer and correctness of this answer
    """
    egrun = EmailGameRuns.objects.get(Q(email_id=email.id) & Q(game_run_id=gamerun.id) &
                                      ~Q(player_answer=None))
    answer_correctness = False

    # if player's answer is False and type of email is legit, answer is correct
    if not egrun.player_answer & (egrun.email.type.id == 1):
        answer_correctness = True
    # if player's answer is True and type of email is phishing, answer is correct
    elif egrun.player_answer & (egrun.email.type.id > 1):
        answer_correctness = True

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