<gh_stars>0
#!/usr/bin/env python

from django.http import HttpResponseRedirect
from django.shortcuts import render
from django.urls import reverse
from django.contrib.auth.decorators import login_required
from django.contrib.auth import authenticate, login

from .models import Question, Reservation, Player, Answer
from .forms import PlayerCreationForm, AnswerCreationForm


def index(request):
    """
    Display the intro page of the app.

    **Template:**

    :template:`quiz/index.html`
    """
    return render(request, 'quiz/index.html')


@login_required()
def question_home(request):
    """
    The home page of the game, display an individual :model:`quiz.Question`.
    Allows the player to reserve it using a button.

    **Context**

    ``online_players``
        A query using the :model:`quiz.Player` manager.
    ``question``
        An instance of :model:`quiz.Question`.
    ``available_questions``
        A query using the :model:`quiz.Question` manager.

    **Template:**

    :template:`quiz/question/home.html`
        If a question is available.
    :template:`quiz/question/no_questions.html`
        If there are no LIVE questions.
    """
    # page templates
    page_template_show_question = 'quiz/question/home.html'
    page_template_no_questions = 'quiz/question/no_questions.html'

    # Check if there are new questions
    # By 'new' it means with status 'live'
    try:
        available_questions = Question.objects.filter(
            status=Question.STATUS_LIVE
        ).order_by('-creation_date')[:1]

        # Get the question to be displayed
        current_question = available_questions.get()

        # select the correct template
        page_template = page_template_show_question

    except Question.DoesNotExist:
        current_question = None
        page_template = page_template_no_questions

    return render(request, page_template, {
        'online_players': Player.objects.get_online_players(),
        'question': current_question,
        'available_questions': Question.objects.questions_available()
    })


@login_required()
def reservation(request, question_id):
    """
    The second step of the the game: the player has reserved a question.
    A :model:`quiz.Reservation` object will be created here
    (only if it's the first time).

    question_id is the id of the :model:`quiz.Question` to show.

    Redirects to :view:`quiz.question_home` if there's no
    :model:`quiz.Question` with question_id.

    **Context**

    ``online_players``
        A query using the :model:`quiz.Player` manager.
    ``question``
        An instance of :model:`quiz.Question`.
    ``available_questions``
        A query using the :model:`quiz.Question` manager.

    **Template:**

    :template:`quiz/question/reservation.html`
    """
    page_template = 'quiz/question/reservation.html'

    # Retrieve the question from the id
    try:
        question = Question.objects.get(pk=question_id)

    except (KeyError, Question.DoesNotExist):
        # TODO: display a message
        # Always return an HttpResponseRedirect after successfully dealing
        # with POST data. This prevents data from being posted twice if a
        # user hits the Back button.
        return HttpResponseRedirect(reverse('quiz:question_home'))

    # Get auth player info
    player = request.user

    # Check if a reservation has already been made by this player
    reservation_query = Reservation.objects.filter(
        question=question,
        player=player
    ).count()

    # If this is the first time and thus it's my first reservation,
    # then create one
    if reservation_query == 0:
        reservation = Reservation.objects.create_reservation(question, player)
        # TODO: display a message

    return render(request, page_template, {
        'online_players': Player.objects.get_online_players(),
        'question': question,
        'available_questions': Question.objects.questions_available()
    })


@login_required()
def reservation_steer(request, question_id):
    """
    This view steers the player based on the reservation approval result.
    If the players won, then they'll be redirected to the next phase,
    where an answer will be created and submitted.
    Otherwise, another view wll show he/she that he/she lost and can
    proceed with a new question.

    question_id is the id of the :model:`quiz.Question` to show.

    Redirect to :view:`quiz.provide_answer` if the player won the reservation,
    otherwise to :view:`quiz.reservation_lost`.
    Redirect to :view:`quiz.question_home` if there's no question with
    question_id.

    **Context**

    ``online_players``
        A query using the :model:`quiz.Player` manager.
    ``question``
        An instance of :model:`quiz.Question`.
    ``available_questions``
        A query using the :model:`quiz.Question` manager.
    """
    try:
        question = Question.objects.get(pk=question_id)

    except (KeyError, Question.DoesNotExist):
        # TODO: display a message
        return HttpResponseRedirect(reverse('quiz:question_home'))

    # Check if the player has won the reservation
    reservation_approved = Reservation.objects.filter(
        question=question, approved=True
    ).get()
    approved_player = str(reservation_approved.player.nickname)

    # Get authenticated/logged (current) player
    current_player = str(request.user)

    # If is the auth user that won, redirect to the answer page
    if approved_player == current_player:
        return HttpResponseRedirect(
            reverse('quiz:provide_answer',
                    args=(question.id, reservation_approved.id))
        )

    else:
        # Otherwise, to the lost page
        return HttpResponseRedirect(reverse(
                'quiz:reservation_lost',
                args=(question.id, approved_player,)
        ))


@login_required()
def reservation_lost(request, question_id, approved_player):
    """
    Display a 'you-lost' message.

    question_id is the id of the :model:`quiz.Question` to show.
    approved_player is the nickname of the :model:`quiz.Player` who
    won the reservation race.

    Redirect to :view:`quiz.question_home` if there's no
    :model:`quiz.Question` with question_id.

    **Context**

    ``online_players``
        A query using the :model:`quiz.Player` manager.
    ``question``
        An instance of :model:`quiz.Question`.
    ``approved_player``
        Nickname of a :model:`quiz.Player`.
    ``available_questions``
        A query using the :model:`quiz.Question` manager.

    **Template:**

    :template:`quiz/question/reservation_lost.html`
    """
    page_template = 'quiz/question_reservation_lost.html'

    try:
        question = Question.objects.get(pk=question_id)

    except (KeyError, Question.DoesNotExist):
        # TODO: display a message
        return HttpResponseRedirect(reverse('quiz:question_home'))

    return render(request, page_template, {
        'online_players': Player.objects.get_online_players(),
        'question': question,
        'approved_player': approved_player,
        'available_questions': Question.objects.questions_available()
    })


@login_required()
def provide_answer(request, question_id, reservation_id):
    """
    Display the current question and the form to provide an answer.
    When data is provided, it creates a :model:`quiz.Answer` object.

    question_id is the id of the :model:`quiz.Question` to show.
    reservation_id is the id of the :model:`quiz.Reservation` linked
    to the auth player and current question.

    Redirect to :view:`quiz.question_home` if there's no
    :model:`quiz.Question` with question_id or :model:`quiz.Reservation`
    with reservation_id.

    **Context**

    ``online_players``
        A query using the :model:`quiz.Player` manager.
    ``question``
        An instance of :model:`quiz.Question`.
    ``form``
        An instance of AnswerCreationForm.
    ``available_questions``
        A query using the :model:`quiz.Question` manager.
    ``disable_form``
        A boolean for disable the button form.
    ``answer_id``
        An id of a :model:`quiz.Answer` object.

    **Template:**

    :template:`quiz/question/answer.html`
    """
    page_template = 'quiz/question/answer.html'

    # Security checks
    try:
        question = Question.objects.get(pk=question_id)
        reservation = Reservation.objects.get(pk=reservation_id)

    except (KeyError, Question.DoesNotExist, Reservation.DoesNotExist):
        # TODO: display a message
        return HttpResponseRedirect(reverse('quiz:question_home'))

    # Get the player who won the reservation
    player = reservation.player

    # Default values: no answers and no form visible
    answer = None

    # This is used to make something visual in the template to indicate
    # that the player has to wait admin
    disable_form = False

    # If data from the post is provided
    if request.method == 'POST':
        # Create an AnswerCreationForm from POST data
        form = AnswerCreationForm(request.POST)

        # First we have to check if an answer already exist.
        # Note: only one answer allowed at this moment.
        if Answer.objects.filter(question=question).count() == 0:
            # If data is valid, create an Answer object
            if form.is_valid():
                answer = form.save(commit=False)

                # Link answer to question and player
                # TODO: Probably there's a better way
                answer.question = question
                answer.player = player

                answer.save()

                disable_form = True

            else:
                disable_form = False

    else:
        # You should think that now an empty form should be created.
        # Usually it is, but there's some cases in which the player
        # has already made an answer, then came back here.
        # In this case it's better to show his/here previous answer

        # So, first query for answer (this is also used to populate answer_id)
        answer_query = Answer.objects.filter(question=question)
        if answer_query:
            answer = answer_query.get()
            form = AnswerCreationForm(initial={
                'answer_text': answer.answer_text,
            })
            # Disable modifications.
            form.fields['answer_text'].disabled = True
            disable_form = True

        else:
            # Otherwise create an empty form to be displayed
            form = AnswerCreationForm()

    return render(request, page_template, {
        'online_players': Player.objects.get_online_players(),
        'question': question,
        'form': form,
        'available_questions': Question.objects.questions_available(),
        'disable_form': disable_form,
        'answer_id': answer.id if answer else None
    })


@login_required()
def answer_steer(request, question_id, answer_id):
    """
    Display a message to the player based on the result of the admin approval
    for the :model:`quiz.Answer` provided.

    question_id is the id of the :model:`quiz.Question` to show.
    answer_id is the id of the :model:`quiz.Answer` provided.

    Redirect to :view:`quiz.question_home` if there's no
    :model:`quiz.Question` with question_id or :model:`quiz.Answer`
    with answer_id.

    **Context**

    ``online_players``
        A query using the :model:`quiz.Player` manager.
    ``question``
        An instance of :model:`quiz.Question`.
    ``available_questions``
        A query using the :model:`quiz.Question` manager.

    **Template:**

    :template:`quiz/question/answer_correct.html`
        If the answer status is APPROVED.
    :template:`quiz/question/answer_wrong.html`
        If the answer status is REJECTED.
    """
    page_template_answer_correct = 'quiz/question/answer_correct.html'
    page_template_answer_wrong = 'quiz/question/answer_wrong.html'

    try:
        question = Question.objects.get(pk=question_id)
        answer = Answer.objects.get(pk=answer_id)

    except (KeyError, Question.DoesNotExist, Answer.DoesNotExist):
        # TODO: display a message
        return HttpResponseRedirect(reverse('quiz:question_home_home'))

    # Render template based on the answer status
    if answer.status == Answer.STATUS_APPROVED:
        return render(request, page_template_answer_correct, {
            'online_players': Player.objects.get_online_players(),
            'question': question,
            'available_questions': Question.objects.questions_available(),
        })

    elif answer.status == Answer.STATUS_REJECTED:
        # Otherwise, to the lost page
        return render(request, page_template_answer_wrong, {
            'online_players': Player.objects.get_online_players(),
            'question': question,
            'available_questions': Question.objects.questions_available(),
        })

    else:
        # We should never be here, but handle it just in case
        # TODO: display a message
        return HttpResponseRedirect(reverse('quiz:question_home'))


def signup(request):
    """
    Display a PlayerCreationForm that allows the creation of a new player
    (not admin).

    Redirect to :view:`quiz.question_home` if the creation and authentication
    went well.
    Redirect to :view:`quiz.login` if an error occurred
    during the authentication.

    **Context**

    ``form``
        An instance of PlayerCreationForm.

    **Template:**

    :template:`quiz/account/signup.html`
    """
    page_template = 'quiz/account/signup.html'

    # If data is provided...
    if request.method == 'POST':
        # Populate the form.
        form = PlayerCreationForm(request.POST)
        if form.is_valid():
            # Create a new player from the form data.
            player = form.save(commit=False)
            player.is_active = True
            player.save()
            # messages.success(request, 'Account created successfully')

            # Try to authenticate the player
            user = authenticate(
                username=player.nickname,
                password=<PASSWORD>.cleaned_data.get('<PASSWORD>')
            )

            # If it worked, then redirect him to the home page.
            if user:
                login(request, user)

                return HttpResponseRedirect(reverse('quiz:question_home'))

            else:
                # Otherwise he'll have to do it manually.
                # messages.warning(request,
                #                  'Something went wrong with the '
                #                  'authentication.'
                #                  )

                return HttpResponseRedirect(reverse('quiz:login'))

    else:
        # If no data (e.g. first time visiting the page),
        # create an empty form.
        form = PlayerCreationForm()

    return render(request, page_template, {
        'form': form,
    })
