"""Utilities for writing forms."""

from django import forms
from django.forms.formsets import ORDERING_FIELD_NAME

from mtp.apps.common.models import Orderable


class MTPBaseInlineFormset(forms.BaseInlineFormSet):
    """Base classes for inlines.

    In a default formset, all the forms are displayed but there is no way to manage them
    dynamically.

    For that, we can use the ``inlines.js`` file from django-admin, which allow for adding and
    removing of new forms.

    For the "adding" stuff to work, the ``inlines.js`` file duplicates a "template".

    This template is available in the formset in the ``empty_form`` var.

    So this base class adds this empty form to the list of form to display in the iterator.

    """
    def __iter__(self):
        """Iterate on all forms plus the empty form."""

        for form in super().__iter__():
            yield form
        yield self.empty_form

    def __len__(self):
        """Return the number of forms including the empty one."""

        return super().__len__() + 1


class OrderableInlineFormset(MTPBaseInlineFormset):
    """A base for inline formsets that allows ordering.

    This works with a template that should include the template
    ``cms/includes/orderable-inline-head.html`` in the ``head`` block. See this template for full
    documentation on how to use it, the available  arguments, and an exemple on how to write your
    html.

    Two other conditions:
    - the model used in the inline should inherit from ``orderable.models.Orderable``.
    - the inline should be created with ``can_order`` set to ``True``.

    """

    def __init__(self, *args, **kwargs):
        """Check attributes before creating the instance.

        Raises
        ------
        AttributeError
            If the ``can_order`` attribute is not set to ``True``.
        AttributeError
            If the ``model`` attribute doesn't inherit from ``Orderable``.
        """

        if not getattr(self, 'can_order', False):
            raise AttributeError('The `can_order` attribute of a `OrderableInlineFormset` '
                                 'must be set to True.')
        if Orderable not in self.model.mro():
            raise AttributeError('The `model` of a `OrderableInlineFormset` must inherit from '
                                 '`Orderable`.')

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

    def save(self, commit=True):
        """Override default save the forms in the right order..

        We use ``ordered_forms`` to get the forms ordered via the ``ORDERING_FIELD_NAME``.
        Then we reset the ``sort_order`` field to let the ``save`` method from ``Orderable``
        order them in the order we save them.
        """
        result = super().save(commit=commit)

        for form in self.ordered_forms:
            form.instance.sort_order = None
            form.instance.save()

        return result

    def add_fields(self, form, index):
        """Hide the "ORDER" field of the given form when all fields are ready."""

        super().add_fields(form, index)

        form.fields[ORDERING_FIELD_NAME].widget = forms.HiddenInput()
