""" This module holds the managers and querysets for the ``common`` app. """

from django.db import models

from mtp.apps.sync.managers import SyncableManager


class ReferenceableManager(models.Manager):
    """The Manager for the ``mtp.apps.common.models.Referenceable`` model.

    Notes
    -----
    This manager should not be used as the default (`objects`) one.

    """

    def get_queryset(self):
        """Get only reference instances."""

        return super().get_queryset().filter(is_reference=True)


class ReferenceableSyncableManager(ReferenceableManager, SyncableManager):
    """The Manager for the models that are ``Referenceable`` AND ``Syncable`.

    Notes
    -----
    This manager should not be used as the default (`objects`) one.

    """

    def get_queryset(self):
        """Get only reference instances that are not deleted"""

        return super().get_queryset().non_deleted()


class CleanDuplicatedManagerMixin(models.Manager):
    """Manager mixin which provide the ``clean_duplicates`` method.

    We use this mixin when we want add a new `unique_together` on a model.
    We advice to create 2 migrations for this:
    - The first clean duplicates instances thanks to this mixin (with RunPython) and test it.
    - The second add the new ``unique_together`` on the model and edit your precedent test.

    """

    def clean_duplicates(self, unique_together=None):
        """Clean duplicated instances according to ``unique_together``.

        Parameters
        ----------
        unique_together: iterator(iterator)
            A nested iterator which represent the fields uniqueness of the model.

        Warnings
        --------
        This method calls ``delete()`` directly on a queryset,
        so the custom ``delete`` method on model isn't executed.

        """

        unique_together = unique_together or self.model._meta.unique_together or []

        for unique_fields in unique_together:

            duplicates = self.values(*unique_fields).order_by().annotate(
                max_id=models.Max('id'),
                count=models.Count('id'),
            ).filter(count__gt=1)

            for duplicate in duplicates:
                self.filter(
                    **{field: duplicate[field] for field in unique_fields}
                ).exclude(id=duplicate['max_id']).delete()
