"""Utils QuerySet Mixins"""
from functools import reduce
from operator import add

from django.db import models
from django.db.models import Q, F

from utils.methods import get_contenttype
from utils.models import TJSONField


class ContentTypeQuerySetMixin(models.QuerySet):
    """QuerySet for ContentType"""

    def by_object_id(self, object_id: int):
        """Filter by object_id"""
        return self.filter(object_id=object_id)

    def by_content_type(self, app_label: str = 'favorites', model: str = 'favorites'):
        """Filter QuerySet by ContentType."""
        return self.filter(content_type=get_contenttype(app_label=app_label,
                                                        model=model))


class RelatedObjectsCountMixin(models.QuerySet):
    """QuerySet for ManyToMany related count filter"""

    def _get_related_objects_names(self):
        """Get all related objects (with reversed)"""
        related_objects_names = []
        for related_object in self.model._meta.related_objects:
            if isinstance(related_object, models.ManyToManyRel):
                related_objects_names.append(related_object.name)

        return related_objects_names

    def _annotate_related_objects_count(self):
        """Annotate all related objects to queryset"""
        annotations = {}
        for related_object in self._get_related_objects_names():
            annotations[f"{related_object}_count"] = models.Count(f"{related_object}")

        return self.annotate(**annotations)

    def filter_related_gt(self, count):
        """QuerySet filter by related objects count"""
        q_objects = Q()
        for related_object in self._get_related_objects_names():
            q_objects.add(Q(**{f"{related_object}_count__gt": count}), Q.OR)

        return self._annotate_related_objects_count().filter(q_objects)

    def filter_all_related_gt(self, count):
        """Queryset filter by all related objects count"""
        exp = reduce(add, [F(f"{related_object}_count") for related_object in self._get_related_objects_names()])
        return self._annotate_related_objects_count()\
            .annotate(all_related_count=exp)\
            .filter(all_related_count__gt=count)


class TranslationQuerysetMixin(models.QuerySet):

    def _get_translatable_fields(self):
        """Get translatable fields from model."""
        translatable_fields = []
        model = self.model
        for field in model._meta.fields:
            field_name = field.name
            if isinstance(field, TJSONField):
                translatable_fields.append(field_name)
        return translatable_fields

    def _get_translatable_field_filters(self, locale: str):
        """Generate filters for filtering TJSON objects that has necessary locale."""
        return {f'{field}__has_key': locale for field in self._get_translatable_fields()}

    def has_translation(self, locale: str):
        """Filter objects by locale from request."""
        return self.filter(**self._get_translatable_field_filters(locale))
