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:

        context = {
            'account_page': 'is-active',
            'submission_tab': 'is-active',
            'ongoing': edition,
        }

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

            return render(request, 'user/submission.html', {'students': queryset, **context})

        queryset = Student.objects.filter(identifier__startswith=f'{edition.year}-{request.user.id}-')
        ArticleFormSet = formset_factory(StudentForm, extra=3, max_num=3, min_num=0)

        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', {'formset': formset, 'students': len(queryset), **context})
    else:
        return redirect(reverse_lazy('account'))


class AboutContestView(TemplateView):
    template_name = 'home.html'
    extra_context = {
        'aboutcontest_page': 'is-active'
    }


class NewsView(TemplateView):
    template_name = 'news.html'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['news_page'] = 'is-active'
        context['announcements'] = Announcement.objects.all()

        return context


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


class TestsView(TemplateView):
    template_name = 'tests.html'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['tests_page'] = 'is-active'
        context['editions'] = Edition.objects.order_by('-year')

        return context


def ScoresView(request, year=None):
    if year is not None:
        edition = get_object_or_404(Edition, year=year)
    else:
        edition = Edition.objects.filter(scores_available=True).order_by('-year').first()

        if edition is None:
            edition = Edition.objects.order_by('-year').first()

        year = edition.year

    context = {
        'scores_page': 'is-active',
        'year': year,
        'scores_available': edition.scores_available or request.user.is_superuser,
        'editions': Edition.objects.order_by('year')
    }

    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 = filter(lambda x: x['score'] >= edition.award_threshold,
                        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)))

    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 = {
        'individual': sorted(individual, key=lambda x: -x['score']),
        'teams': sorted(teams, key=lambda x: -x['score']),
        **context
    }

    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'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['latest'] = Announcement.objects.first()

        return context
