""" This module holds the forms of the ``evaluations`` app. """

from django import forms
from django.forms.models import inlineformset_factory
from django.utils.translation import ugettext_lazy as _

from mtp.apps.common.forms_utils import OrderableInlineFormset

from .models import Criterion, Evaluation, Exercise


class EvaluationForm(forms.ModelForm):
    """This form allows to create and edit evaluations."""

    class Meta:
        model = Evaluation
        fields = (
            'name',
            'evaluation_type',
        )


class ExerciseForm(forms.ModelForm):
    """This form allows to create and edit exercises.

    Parameters
    ----------
    For the parameters, see ``django.forms.ModelForm``

    """

    class Meta:
        model = Exercise
        fields = (
            'name',
            'description',
            'start_range',
            'end_range',
            'step',
        )

    def __init__(self, *args, **kwargs):
        """Initialize the form with the provided evaluation.

        We expect "evaluation" to be part of the kwargs.

        """

        self.evaluation = kwargs.pop('evaluation')

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

    def save(self, commit=True):
        """Set the instance evaluation

        For the parameters, see ``django.forms.ModelForm``

        """

        self.instance.evaluation = self.evaluation

        return super().save(commit=commit)


class ExercisesDeleteForm(forms.Form):
    """Form to select and soft delete a list of ``Exercise`` instances

    We expect ``evaluation`` to be part of the ``kwargs``.

    For the parameters, see ``django.forms.ModelForm``

    """

    exercises = forms.ModelMultipleChoiceField(
        queryset=Exercise.objects.non_deleted()
    )

    def __init__(self, *args, **kwargs):
        """Initialize the form with the evaluation and update the exercises queryset accordingly

        For the parameters, see ``django.forms.ModelForm``"""

        evaluation = kwargs.pop('evaluation', None)

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

        self.fields['exercises'].queryset = evaluation.exercises.non_deleted()


class CriterionForm(forms.ModelForm):
    """This form allows to create and edit criteria."""

    description = forms.CharField(
        label=_('Description')
    )

    criterion_type = forms.ChoiceField(
        label=_('criterion type'),
        choices=Criterion.CRITERION_TYPES.choices,
        initial=Criterion.CRITERION_TYPES.QUANTITATIVE
    )

    class Meta:
        model = Criterion
        fields = (
            'criterion_type',
            'description',
        )

    def clean_description(self):
        """Clean the description field accordingly to the criterion_type

        Raises
        ------
        forms.ValidationError
            If the number of characters in the description field don't match
            the max allowed for the criterion type.

        """

        description = self.cleaned_data['description']
        criterion_type = self.cleaned_data['criterion_type']

        if len(description) > Criterion.CRITERION_MAX_CHARS[criterion_type]:
            raise forms.ValidationError(
                _('A %(type)s criterion should contains maximum %(max_char)d characters') % {
                    'type': Criterion.CRITERION_TYPES.for_value(criterion_type).display,
                    'max_char': Criterion.CRITERION_MAX_CHARS[criterion_type]
                }
            )

        return description


EvaluationExercisesCriteriaFormSet = inlineformset_factory(  # pylint: disable=invalid-name
    Exercise,
    Criterion,
    form=CriterionForm,
    formset=OrderableInlineFormset,
    extra=0,  # Be sure to have all forms displayed even if no criterion exist.
    min_num=0,
    can_delete=True,  # Default value but explicit is better than implicit,
    can_order=True,  # Default to False but here we want ordering
)
