"""This module holds the filters for the ``trainings`` app"""

from django import forms
from django.contrib.contenttypes.models import ContentType
from django.utils.translation import ugettext_lazy as _
from django.db import models

import django_filters
from django_select2.forms import Select2MultipleWidget
from waffle import switch_is_active

from mtp.apps.accounts.models import Group
from mtp.apps.common.filters import MTPFilterSet
from mtp.apps.common.widgets import DateRangeInput

from .models import (
    Training,
    TrainingActivity,
    TrainingSession
)


class TrainingFilter(django_filters.ModelMultipleChoiceFilter):
    """Filter field for the ``Training`` model used in the ``TrainingSessionFilterForm``"""

    def filter(self, qs, value):  # pylint: disable=method-hidden
        """Filter a ``TrainingSessionQuerySet``, accordingly the the provided value

        Parameters
        ----------
        qs: TrainingSessionQuerySet
            The QuerySet to filter
        value: iterable or None
            A list of ``Training`` pks

        Returns
        -------
        ``TrainingSessionQuerySet``
            The filtered QuerySet

        """

        value = value or ()  # Make sure we have an iterable

        if self.is_noop(qs, value):
            return qs

        # Even though not a noop, no point filtering if empty
        if not value:
            return qs

        # Get the original version of the provided trainings pks
        original_pks = [
            training.original_version_id or training.pk
            for training in value
        ]

        # Filter out the sessions accordingly to their original and the user's roles
        qs = qs.filter(
            models.Q(
                training__original_version_id__in=original_pks
            ) | models.Q(
                training__id__in=original_pks
            )
        )

        return qs


class TrainingSessionFilter(MTPFilterSet):
    """Filter for the ``TrainingSession`` list view

    Attributes
    ----------
    training: ``ModelMultipleChoiceFilter`` instance
        Filter field for the session ``training`` attribute
    start_date: ``DateFromToRangeFilter``
        Filter field for the session ``start_date`` attribute

    """

    STATE_EMPTY_CHOICE = ('', _('Any'))

    training = TrainingFilter(
        label=_('Training'),
        widget=Select2MultipleWidget(
            attrs={'data-allow-clear': 'false'}
        )
    )

    training__business_unit = django_filters.ModelMultipleChoiceFilter(
        label=_('Business units'),
        widget=Select2MultipleWidget(
            attrs={'data-allow-clear': 'false'}
        )
    )

    start_date = django_filters.DateFromToRangeFilter(
        label=_('Date'),
        widget=DateRangeInput(
            attrs={'type': 'date'}
        )
    )

    class Meta:
        model = TrainingSession
        fields = ['training', 'training__business_unit', 'state', 'start_date']

    def __init__(self, *args, **kwargs):
        """Update the filters's fields accordingly to the provided kwargs

        Also, we add a new ``STATE_EMPTY_CHOICE`` to the choices of the state field.

        For the parameters, see ``django_filters.FilterSet``

        """

        trainings_queryset = kwargs.pop('trainings_queryset')
        user = kwargs.pop('user')

        super().__init__(*args, **kwargs)

        # Update the ``training`` field arguments
        self.filters['training'].extra.update({
            'queryset': trainings_queryset,
        })

        # Update the choices of the ``state`` field, and set the initial one
        self.filters['state'].extra.update({
            'choices': (self.STATE_EMPTY_CHOICE,) + tuple(
                self.filters['state'].extra['choices']
            ),
            'initial': '',
        })

        user_managed_job_profile_pks = Group.objects.job_profiles().managed_by(
            user
        ).values_list('pk', flat=True)

        # Add the business_unit filter if the current user is a manager of a job_profile
        if user_managed_job_profile_pks:

            # Since the we don't have any hierarchy regarding the groups,
            # and especially between a job profile and the linked business units,
            # We need these two requests below
            business_unit_pks = Training.objects.filter(
                job_profile_id__in=user_managed_job_profile_pks
            ).values_list('business_unit_id', flat=True)

            self.filters['training__business_unit'].extra.update({
                'queryset': Group.objects.business_units().filter(id__in=business_unit_pks)
            })
        else:
            del self.filters['training__business_unit']


class TrainingFilterSet(MTPFilterSet):
    """Filter Set for the ``TrainingListView``"""

    TRAINING_LIST_FILTERS = Training.VERSION_STATES.extract_subset(
        'DRAFT', 'PUBLISHED', 'ARCHIVED'
    )

    name = django_filters.CharFilter(
        lookup_expr='icontains',
        label=_('Name')
    )

    version_state = django_filters.MultipleChoiceFilter(
        choices=TRAINING_LIST_FILTERS,
        label=_('Status'),
        widget=Select2MultipleWidget()
    )

    class Meta:
        model = Training
        fields = [
            'name',
            'job_profile',
            'business_unit',
            'version_state',
        ]

    def __init__(self, *args, **kwargs):
        """Update the filters's fields accordingly to the provided kwargs"""

        job_profile_queryset = kwargs.pop('job_profile_queryset')
        business_unit_queryset = kwargs.pop('business_unit_queryset')

        super().__init__(*args, **kwargs)

        # If the assessment feature is off, remove the job_profile field
        if not switch_is_active('enable_assessments'):
            self.filters.pop('job_profile')
        else:
            # Update the queryset of the ``job_profile`` field
            self.filters['job_profile'].extra.update({
                'queryset': job_profile_queryset,
            })

        # Update the queryset of the ``business_unit`` field
        self.filters['business_unit'].extra.update({
            'queryset': business_unit_queryset,
        })


class TrainingActivityFilterSet(MTPFilterSet):
    """FilterSet for the ``TrainingActivity`` models

    For the parameters, see ``django_filters.FilterSet``

    """

    name = django_filters.CharFilter(
        lookup_expr='icontains',
        label=_('Name'),
        widget=forms.TextInput(
            attrs={'placeholder': _('Filters')}
        )

    )

    polymorphic_ctype_id = django_filters.MultipleChoiceFilter(
        label=_('Type'),
        widget=Select2MultipleWidget()
    )

    class Meta:
        model = TrainingActivity
        fields = [
            'name',
            'polymorphic_ctype_id'
        ]

    def __init__(self, *args, **kwargs):
        """Update the choices of the "type" field"""

        super().__init__(*args, **kwargs)

        activity_choices = [
            (
                ContentType.objects.get_for_model(activity_cls).id,
                activity_cls.verbose_activity_name,
            )
            for activity_cls in TrainingActivity.available_activity_models()
        ]

        self.filters['polymorphic_ctype_id'].extra.update({
            'choices': activity_choices
        })
