from django.forms import formset_factory
from django.shortcuts import render, redirect, get_object_or_404
from django.urls import reverse_lazy
from django.views.generic.edit import CreateView
from django.views.generic import TemplateView
from django.contrib.auth.decorators import login_required
from django.db import transaction
from functools import reduce

from .forms import CustomUserCreationForm, CustomUserChangeForm, StudentForm
from .models import Announcement, Edition, Student


class SignUpView(CreateView):
    form_class = CustomUserCreationForm
    success_url = reverse_lazy('login')
    template_name = 'registration/signup.html'


@login_required
def UpdateAccountView(request):
    if request.method == 'POST':
        form = CustomUserChangeForm(request.POST, instance=request.user)
        if form.is_valid():
            form.save()
    else:
        form = CustomUserChangeForm(instance=request.user)

    return render(request, 'user/account.html', {'account_page': 'is-active', 'account_tab': 'is-active', 'ongoing': Edition.current(), 'form': form})


@login_required
def SubmitStudentsView(request):
    edition = Edition.current()

    if edition is not None:
        identifier = f'{edition.year}-{request.user.id}-'
        queryset = Student.objects.filter(identifier__startswith=identifier)

        if edition.submissions is not True:
            identifier = f'{edition.year}-{request.user.id}'
            queryset = Student.objects.filter(identifier__startswith=identifier).order_by('identifier')
            return render(request, 'user/submission.html', {'account_page': 'is-active', 'submission_tab': 'is-active', 'ongoing': edition, 'students': queryset})

        ArticleFormSet = formset_factory(StudentForm, extra=2, max_num=3, min_num=1)

        if request.method == 'POST':
            with transaction.atomic():
                formset = ArticleFormSet(request.POST)
                if formset.is_valid():
                    queryset.delete()

                    students = filter(lambda x: x != {}, formset.cleaned_data)
                    for index, data in enumerate(students):
                        Student.objects.update_or_create(identifier=f'{edition.year}-{request.user.id}-{index + 1}', defaults=data)
        else:
            formset = ArticleFormSet(initial=list(map(lambda x: x.__dict__, queryset)))

        return render(request, 'user/submit.html', {'account_page': 'is-active', 'submission_tab': 'is-active', 'ongoing': True, 'formset': formset})
    else:
        return redirect(reverse_lazy('account'))


class HomeView(TemplateView):
    template_name = 'home.html'


class NewsView(TemplateView):
    template_name = 'news.html'
    extra_context = {
        'news_page': 'is-active',
        'announcements': Announcement.objects.all(),
    }


class RulesView(TemplateView):
    template_name = 'rules.html'
    extra_context = {
        'rules_page': 'is-active'
    }


class TestsView(TemplateView):
    template_name = 'tests.html'
    extra_context = {
        'tests_page': 'is-active',
        'editions': Edition.objects.order_by('-year')
    }


def ScoresRedirect(request):
    latest = Edition.objects.filter(scores_available=True).order_by('-year').first()

    if latest is not None:
        return redirect(reverse_lazy('scores', args=[latest.year]))
    else:
        return redirect(reverse_lazy('home'))


def ScoresView(request, year):
    edition = get_object_or_404(Edition, year=year)

    if edition.scores_available is not True:
        return redirect('scores')

    students = Student.objects.filter(identifier__startswith=year)

    schools = map(lambda x: {
        'id': x.identifier.split('-')[1],
        'name': x.school_name,
        'town': x.school_town
    }, students)
    schools = list({school['id']: school for school in schools}.values())

    individual = map(lambda x: {
        **x.__dict__,
        'score': x.score_first * x.score_second,
        'title': 'Laureat' if x.score_first * x.score_second >= edition.laureate_threshold else 'Wyróżnienie'
    }, students.filter(score_first__gte=edition.entry_threshold))
    individual = filter(lambda x: x['score'] >= edition.award_threshold, individual)

    teams = []
    for school in schools:
        team = students.filter(identifier__startswith=f'{year}-' + school['id'] + '-')

        score_first = reduce(lambda x, y: x + y.score_first, team, 0)
        score_second = reduce(lambda x, y: x + y.score_second, team, 0)

        teams.append({**school, 'score': score_first * score_second})

    context = {
        'scores_page': 'is-active',
        'year': year,
        'editions': Edition.objects.filter(scores_available=True),
        'individual': sorted(individual, key=lambda x: -x['score']),
        'teams': sorted(teams, key=lambda x: -x['score'])
    }

    return render(request, 'scores.html', context)


class ContactView(TemplateView):
    template_name = 'contact.html'
    extra_context = {
        'contact_page': 'is-active'
    }


class RobotsView(TemplateView):
    template_name = 'robots.txt'
    content_type = 'text/plain'


class SitemapView(TemplateView):
    template_name = 'sitemap.xml'
    content_type = 'text/xml'
    extra_context = {
        'latest': Announcement.objects.first()
    }
