from urllib.parse import urlparse, parse_qs

from articles.models import StaticVideo
from django.db.models.aggregates import Sum
from django.db.models.expressions import Case, When
from django.db.models.fields import IntegerField
from django.db.models.query import Prefetch
from django.db.models.query_utils import Q
from django.http.response import HttpResponseRedirect
from django.shortcuts import redirect
from django.urls.base import reverse_lazy
from django.views.generic.base import TemplateView, ContextMixin
from django.views.generic.detail import DetailView
from django.views.generic.edit import FormView
from exercises.models import Exercise
from exercises.views import NotificationContent
from tasks.constants import IntroPopupType, IntroPopupData, NotificationType
from tasks.forms import ApprenticeTaskAnswerForm
from tasks.models import Path, Task, ApprenticeTaskAnswer, ExerciseTaskPosition
from users.decorators import apprentice_login_required
from utils.general import progress_percent


class IntroPopupContextMixin(ContextMixin):
    intro_popup_type = None

    def get_context_data(self, **kwargs):
        context = super(IntroPopupContextMixin, self).get_context_data(**kwargs)
        if self.intro_popup_type is not None and self.intro_popup_type not in self.request.user.intro_videos:
            if self.request.user.intro_videos:
                self.request.user.intro_videos.append(self.intro_popup_type)
            else:
                self.request.user.intro_videos = [self.intro_popup_type]
            self.request.user.save()

            static_video = StaticVideo.objects.filter(system_id=self.intro_popup_type).last()
            if static_video:
                url_data = urlparse(static_video.yt_video)
                query = parse_qs(url_data.query)
                video_id = query["v"][0]
                text = static_video.text
            else:
                popup_data = IntroPopupData.POPUP_DATA.get(self.intro_popup_type)
                video_id = popup_data.get('video_id')
                text = popup_data.get('text')

            if video_id:
                context['intro_popup_video_id'] = video_id
                context['intro_popup_text'] = text
        return context


class ActivePathsContextMixin(ContextMixin):
    """Mixin uzupełniający context o dostępne ścieżki"""

    def get_context_data(self, **kwargs):
        context = super(ActivePathsContextMixin, self).get_context_data(**kwargs)
        context['active_paths'] = Path.objects.active()
        return context


@apprentice_login_required
class PathListView(TemplateView, ActivePathsContextMixin, IntroPopupContextMixin):
    """Widok reprezentujący moduł Ucz się. Umożliwiającą przejście do wybranej ścieżki"""
    template_name = 'tasks/path_list.html'
    intro_popup_type = IntroPopupType.PATHS

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['active_paths'] = context['active_paths'].annotate(
            completed=Sum(Case(When(tasks__answers__apprentice=self.request.user, then=1), default=0),
                          output_field=IntegerField()))
        return context


@apprentice_login_required
class PathDetailView(DetailView, ActivePathsContextMixin):
    """Widok reprezentujący wybraną ścieżkę"""
    template_name = 'tasks/path.html'
    pk_url_kwarg = 'path_id'
    context_object_name = 'selected_path'

    def get_queryset(self):
        return Path.objects.active()

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        apprentice = self.request.user
        tasks = self.object.tasks.active(). \
            prefetch_related(Prefetch('exercise_task_positions',
                                      queryset=ExerciseTaskPosition.objects.active().annotate(done=Sum(
                                          Case(When(exercise__apprentices=apprentice, then=1), default=0,
                                               output_field=IntegerField()))),
                                      to_attr='exercise_task_positions_by_apprentice')).annotate(done_count=Sum(
            Case(When(exercise_task_positions__exercise__apprentices=apprentice, then=1), default=0,
                 output_field=IntegerField()))).prefetch_related(
            Prefetch('answers', queryset=ApprenticeTaskAnswer.objects.filter(apprentice=apprentice)))

        context['tasks'] = tasks
        context['all_exercises_count'] = Exercise.objects.from_path_count(self.object)
        context['done_exercises_count'] = Exercise.objects.from_path(self.object). \
            annotated_with_done_by_apprentice_count(self.request.user)
        available_tasks = self.object.tasks.active().count()
        context['available_tasks'] = available_tasks
        completed_tasks = ApprenticeTaskAnswer.objects.from_path(self.object).by_apprentice(
            self.request.user).completed().count()
        context['completed_tasks'] = completed_tasks
        context['path_progress'] = progress_percent(completed_tasks, available_tasks)
        return context


@apprentice_login_required
class TaskAnswerListView(TemplateView, IntroPopupContextMixin, ActivePathsContextMixin):
    """Widok reprezentujący listę odpowiedzi na zadania (moje filmy oraz oceniaj)"""
    template_name = 'tasks/task_answer_list.html'
    intro_popup_type = IntroPopupType.VIDEO_LIST
    elements_quant = 9

    def process_with_query_attributes(self, queryset):
        attributes = self.request.GET
        query_context = {}
        query = attributes.get('query')
        if query:
            queryset = queryset.filter(
                Q(custom_title__icontains=query) | Q(task__title__icontains=query) | Q(task__subtitle__icontains=query))
            query_context['query'] = query
        try:
            selected_path = int(attributes.get('selected_path'))
            if selected_path:
                queryset = queryset.filter(task__path_id=selected_path)
                query_context['selected_path'] = selected_path
        except (ValueError, TypeError):
            pass
        sort = attributes.get('sort')
        if sort:
            sort_type = 'created' if sort == 'newest' else '-created'
            queryset = queryset.order_by(sort_type)
            query_context['sort'] = sort
        else:
            queryset = queryset.order_by('created')
        count = attributes.get('count')
        if count:
            try:
                count = int(count)
                more = attributes.get('more')
                if more:
                    count += self.elements_quant
            except (ValueError, TypeError):
                count = self.elements_quant
        else:
            count = self.elements_quant
        query_context['count'] = count
        query_context['show_more'] = True if count < queryset.count() else False

        return queryset, query_context

    def get_context_data(self, **kwargs):
        context = super(TaskAnswerListView, self).get_context_data(**kwargs)
        context['notification_context'] = NotificationContent.from_type(NotificationType.TASK_ANSWER_LIST_GOTO)
        context['no_task_answers_notification_context'] = NotificationContent.from_type(
            NotificationType.NO_TASK_ANSWERS)
        context['navigation_item'] = self.request.resolver_match.url_name

        apprentice = self.request.user
        if self.request.resolver_match.url_name == 'own-video-list':
            answers = ApprenticeTaskAnswer.objects.own(apprentice)
        elif self.request.resolver_match.url_name == 'other-video-list':
            answers = ApprenticeTaskAnswer.objects.others(apprentice)
        else:
            answers = ApprenticeTaskAnswer.objects.none()
        answers, query_context = self.process_with_query_attributes(answers)
        context['task_answers'] = answers
        context.update(query_context)
        return context


@apprentice_login_required
class TaskAnswerDetailView(DetailView):
    """Widok reprezentujący odpowiedz do zadania, szczegoly z widoku moje filmy i oceniaj"""
    template_name = 'tasks/task_answer.html'
    pk_url_kwarg = 'task_answer_id'
    context_object_name = 'selected_task_answer'

    def get_context_data(self, **kwargs):
        context = super(TaskAnswerDetailView, self).get_context_data(**kwargs)
        apprentice = self.request.user
        own_videos = self.object.apprentice == apprentice
        context['navigation_item'] = 'own-video-list' if own_videos else 'other-video-list'
        context['vote_list'] = True if own_videos or self.request.user in \
                                                     self.object.video_2.apprentices.all() else False
        context['random_task_answer_from_task'] = ApprenticeTaskAnswer.objects.random_from_task_answer(self.object, 3)
        return context

    def get_queryset(self):
        return ApprenticeTaskAnswer.objects.completed()


class TaskNotificationsContentContextMixin(ContextMixin):
    def get_context_data(self, **kwargs):
        context = super(TaskNotificationsContentContextMixin, self).get_context_data(**kwargs)
        context['vote_notification_context'] = NotificationContent.from_type(NotificationType.TASK_ANSWER_VOTE)
        context['mentor_notification_context'] = NotificationContent.from_type(NotificationType.TASK_ANSWER_MENTOR)
        context['articles_notification_context'] = NotificationContent.from_type(NotificationType.TASK_ANSWER_ARTICLES)
        context['exercises_notification_context'] = NotificationContent.from_type(
            NotificationType.TASK_ANSWER_EXERCISES)
        return context


@apprentice_login_required
class TaskDetailView(DetailView, FormView, ActivePathsContextMixin, TaskNotificationsContentContextMixin):
    """Widok reprezentujący wybrane zadanie"""
    template_name = 'tasks/task.html'
    pk_url_kwarg = 'task_id'
    context_object_name = 'selected_task'
    form_class = ApprenticeTaskAnswerForm

    def dispatch(self, request, *args, **kwargs):
        apprentice = request.user
        self.object = self.get_object()
        task = self.object
        if task.path.is_path_blocking:
            try:
                task_answer = ApprenticeTaskAnswer.objects.get(task=task, apprentice=apprentice)
                return redirect(reverse_lazy('tasks:task-submit-detail', kwargs={'task_answer_slug': task_answer.slug(),
                                                                                 'task_answer_id': task_answer.id}))
            except ApprenticeTaskAnswer.MultipleObjectsReturned:
                return redirect(reverse_lazy('path:path-list'))
                # TODO: LOG
            except ApprenticeTaskAnswer.DoesNotExist:
                if not task.get_in_path_position() - 1 == apprentice.answers.completed().filter(
                        task__position__lt=task.position, task__path=task.path).count():
                    return redirect(reverse_lazy('tasks:path-detail', kwargs={'path_slug': task.path.slug(),
                                                                              'path_id': task.path.id}))
        return super(TaskDetailView, self).dispatch(request, *args, **kwargs)

    def get_queryset(self):
        return Task.objects.active()

    def get(self, request, *args, **kwargs):
        context = self.get_context_data(object=self.object)
        return self.render_to_response(context)

    def form_valid(self, form):
        apprentice = self.request.user
        task = self.get_object()
        if task.path.is_path_blocking:
            try:
                task_answer = ApprenticeTaskAnswer.objects.get(apprentice=apprentice, task=task)
                # Przypadek w którym ścieżka jest blokująca i istnieje już jedna odpowiedz do tego zadania a mimo to
                # ktos probuje wgrac kolejna - nastepuje przekierowanie do tej istniejacej juz instancji
                # TODO: LOG
                return HttpResponseRedirect(reverse_lazy('tasks:task-submit-detail',
                                                         kwargs={'task_answer_slug': task_answer.slug(),
                                                                 'task_answer_id': task_answer.id}))
            except ApprenticeTaskAnswer.MultipleObjectsReturned:
                return HttpResponseRedirect(reverse_lazy('tasks:path-detail',
                                                         kwargs={'path_slug': task.path.slug(),
                                                                 'path_id': task.path.id}))
                # TODO: LOG
            except ApprenticeTaskAnswer.DoesNotExist:
                pass
                # Ok, to znaczy, że dodajemy odpowiedź do tego zadania
        apprentice_video, custom_title = form.save()
        task_answer = ApprenticeTaskAnswer.objects.create(apprentice=apprentice, task=task,
                                                          video_1=apprentice_video,
                                                          custom_title=custom_title)
        self.request.session['first_submit'] = True
        return HttpResponseRedirect(reverse_lazy('tasks:task-submit-detail',
                                                 kwargs={'task_answer_slug': task_answer.slug(),
                                                         'task_answer_id': task_answer.id}))

    def get_context_data(self, **kwargs):
        context = super(TaskDetailView, self).get_context_data(**kwargs)
        apprentice = self.request.user
        tasks = self.object.path.tasks.active().prefetch_related(
            Prefetch('answers', queryset=ApprenticeTaskAnswer.objects.filter(apprentice=apprentice)))
        context['tasks'] = tasks
        context['task_progress'] = 0
        return context


@apprentice_login_required
class TaskAnswerSubmittingView(DetailView, FormView, ActivePathsContextMixin, TaskNotificationsContentContextMixin):
    """Widok reprezentujący wybrane zadanie"""
    template_name = 'tasks/task.html'
    pk_url_kwarg = 'task_answer_id'
    context_object_name = 'task_answer'
    model = ApprenticeTaskAnswer
    form_class = ApprenticeTaskAnswerForm

    def post(self, request, *args, **kwargs):
        self.object = self.get_object()
        form = self.get_form()
        if form.is_valid():
            return self.form_valid(form)
        else:
            return self.form_invalid(form)

    def get_queryset(self):
        return ApprenticeTaskAnswer.objects.filter(apprentice=self.request.user)

    def form_valid(self, form):
        apprentice_video, custom_title = form.save()
        task_answer = self.object
        task_answer.video_2 = apprentice_video
        task_answer.custom_title = custom_title
        task_answer.save()
        self.request.session['second_submit'] = True
        return HttpResponseRedirect(reverse_lazy('tasks:task-submit-detail',
                                                 kwargs={'task_answer_slug': task_answer.slug(),
                                                         'task_answer_id': task_answer.id}))

    def get_context_data(self, **kwargs):
        context = super(TaskAnswerSubmittingView, self).get_context_data(**kwargs)
        apprentice = self.request.user
        tasks = self.object.task.path.tasks.active().prefetch_related(
            Prefetch('answers', queryset=ApprenticeTaskAnswer.objects.filter(apprentice=apprentice)))
        context['tasks'] = tasks
        context['first_submit'] = self.request.session.pop('first_submit', None)
        context['second_submit'] = self.request.session.pop('second_submit', None)
        context['selected_task'] = self.object.task
        context['exercise_task_positions'] = self.object.task.exercise_task_positions. \
            annotated_with_done_by_apprentice(self.request.user)
        context['task_progress'] = self.object.get_task_process()
        context['all_exercises_done'] = self.object.task.all_exercises_done(self.request.user)
        context['random_task_answer_from_task_answer'] = ApprenticeTaskAnswer.objects.random_from_task_answer(
            self.object, 3)
        return context
