"""Querysets and managers for the ``Versionable`` and ``Dependency`` abstract models."""

from mtp.discrete_apps.duplicable.managers import DuplicableManager, DuplicableQuerySet


class VersionableQuerySet(DuplicableQuerySet):
    """Queryset for the ``Versionable`` model."""

    def prepare_state_choices_for_in_statement(self, states):
        """Convert states that are "choices" tuples into their value.

        Parameters
        ----------
        states : list
            List of values from ``Versionable.VERSION_STATES`` for which we want to filter data.
            It can also be a "subset" of ``Versionable.VERSION_STATES`` (like ``DRAFTABLE``, for
            example) in which case we extract the values.

        Returns
        -------
        list
            List of usable values to use in ``in`` statements in querysets

        """

        return [
            (state[0] if isinstance(state, tuple) else state)
            for state in states
        ]

    def filter_in_state(self, states):
        """Filter Versionable instances having a ``version_state`` in the given ones

        Parameters
        ----------
        states : list
            List of values from ``Versionable.VERSION_STATES`` for which we want to filter data.

        Returns
        -------
        VersionableQuerySet
            A clone of the original queryset (``self``), filtered.

        """

        return self.filter(version_state__in=self.prepare_state_choices_for_in_statement(states))

    def exclude_in_state(self, states):
        """Exclude Versionable instances having a ``version_state`` in the given ones

        Parameters
        ----------
        states : list
            List of values from ``Versionable.VERSION_STATES`` for which we want to exclude data.

        Returns
        -------
        VersionableQuerySet
            A clone of the original queryset (``self``), filtered.

        """

        return self.exclude(version_state__in=self.prepare_state_choices_for_in_statement(states))

    def filter_is_editable(self):
        """Filter Versionable instances having a ``version_state`` in the ``EDITABLE`` subset.

        Returns
        -------
        VersionableQuerySet
            A clone of the original queryset (``self``), filtered.

        """

        return self.filter_in_state(self.model.VERSION_STATES.EDITABLE)

    def exclude_is_editable(self):
        """Exclude Versionable instances having a ``version_state`` in the ``EDITABLE`` subset.

        Returns
        -------
        VersionableQuerySet
            A clone of the original queryset (``self``), filtered.

        """

        return self.exclude_in_state(self.model.VERSION_STATES.EDITABLE)

    def filter_is_draftable(self):
        """Filter Versionable instances having a ``version_state`` in the ``DRAFTABLE`` subset.

        Returns
        -------
        VersionableQuerySet
            A clone of the original queryset (``self``), filtered.

        """

        return self.filter_in_state(self.model.VERSION_STATES.DRAFTABLE)

    def exclude_is_draftable(self):
        """Exclude Versionable instances having a ``version_state`` in the ``DRAFTABLE`` subset.

        Returns
        -------
        VersionableQuerySet
            A clone of the original queryset (``self``), filtered.

        """

        return self.exclude_in_state(self.model.VERSION_STATES.DRAFTABLE)

    def filter_is_publishable(self):
        """Filter Versionable instances having a ``version_state`` in the ``PUBLISHABLE`` subset.

        Returns
        -------
        VersionableQuerySet
            A clone of the original queryset (``self``), filtered.

        """

        return self.filter_in_state(self.model.VERSION_STATES.PUBLISHABLE)

    def exclude_is_publishable(self):
        """Exclude Versionable instances having a ``version_state`` in the ``PUBLISHABLE`` subset.

        Returns
        -------
        VersionableQuerySet
            A clone of the original queryset (``self``), filtered.

        """

        return self.exclude_in_state(self.model.VERSION_STATES.PUBLISHABLE)

    def filter_is_draft(self):
        """Filter Versionable instances having a ``version_state`` in the ``DRAFT`` state.

        Returns
        -------
        VersionableQuerySet
            A clone of the original queryset (``self``), filtered.

        """

        return self.filter(version_state=self.model.VERSION_STATES.DRAFT)

    def exclude_is_draft(self):
        """Exclude Versionable instances having a ``version_state`` in the ``DRAFT`` state.

        Returns
        -------
        VersionableQuerySet
            A clone of the original queryset (``self``), filtered.

        """

        return self.exclude(version_state=self.model.VERSION_STATES.DRAFT)

    def filter_is_published(self):
        """Filter Versionable instances having a ``version_state`` in the ``PUBLISHED`` state.

        Returns
        -------
        VersionableQuerySet
            A clone of the original queryset (``self``), filtered.

        """

        return self.filter(version_state=self.model.VERSION_STATES.PUBLISHED)

    def exclude_is_published(self):
        """Exclude Versionable instances having a ``version_state`` in the ``PUBLISHED`` state.

        Returns
        -------
        VersionableQuerySet
            A clone of the original queryset (``self``), filtered.

        """

        return self.exclude(version_state=self.model.VERSION_STATES.PUBLISHED)

    def filter_is_copied(self):
        """Filter Versionable instances having a ``version_state`` in the ``COPIED`` state.

        Returns
        -------
        VersionableQuerySet
            A clone of the original queryset (``self``), filtered.

        """

        return self.filter(version_state=self.model.VERSION_STATES.COPIED)

    def exclude_is_copied(self):
        """Exclude Versionable instances having a ``version_state`` in the ``COPIED`` state.

        Returns
        -------
        VersionableQuerySet
            A clone of the original queryset (``self``), filtered.

        """

        return self.exclude(version_state=self.model.VERSION_STATES.COPIED)

    def filter_is_archived(self):
        """Filter Versionable instances having a ``version_state`` in the ``ARCHIVED`` state.

        Returns
        -------
        VersionableQuerySet
            A clone of the original queryset (``self``), filtered.

        """

        return self.filter(version_state=self.model.VERSION_STATES.ARCHIVED)

    def exclude_is_archived(self):
        """Exclude Versionable instances having a ``version_state`` in the ``ARCHIVED`` state.

        Returns
        -------
        VersionableQuerySet
            A clone of the original queryset (``self``), filtered.

        """

        return self.exclude(version_state=self.model.VERSION_STATES.ARCHIVED)


class VersionableManager(DuplicableManager.from_queryset(VersionableQuerySet)):
    """Default manager for ``Versionable`` models."""
    pass


class DependencyQuerySet(DuplicableQuerySet):
    """Queryset for the ``Versionable`` model."""
    pass

class DependencyManager(DuplicableManager.from_queryset(DependencyQuerySet)):
    """Default manager for ``Dependency`` models."""
    pass


# Manage queryset and manager for django-polymorphic, that is old and so cannot
# manage correctly the ``from_queryset`` used to simply create a manager from a QuerySet class
try:
    # Disable pylint "unused-import" because this import is just to set ``TEST_POLYMORPHIC``
    # pylint: disable=W0621
    from polymorphic.manager import PolymorphicManager, PolymorphicQuerySet
    # pylint: disable=W0611
    from mtp.discrete_apps.duplicable.managers import PolymorphicManagerForDuplicable
except ImportError:
    # django-polymorphic is not installed so we don't need to manage it
    pass
else:
    class PolymorphicQuerySetForVersionable(VersionableQuerySet, PolymorphicQuerySet):
        """A QuerySet to use for models that are polymorphic AND versionable."""
        pass

    class PolymorphicManagerForVersionable(PolymorphicManager):
        """A Manager to use for models that are polymorphic AND versionable."""
        queryset_class = PolymorphicQuerySetForVersionable

    class PolymorphicQuerySetForDependency(DependencyQuerySet, PolymorphicQuerySet):
        """A QuerySet to use for models that are polymorphic AND dependency."""
        pass

    class PolymorphicManagerForDependency(PolymorphicManager):
        """A Manager to use for models that are polymorphic AND dependency."""
        queryset_class = PolymorphicQuerySetForDependency
