import itertools
import operator

from django.apps import apps
from django.db import models
from django.db.models import Q
from django.db.models.query import QuerySet


class FolderManager(models.Manager):

    def members(self, folder, **kwargs):
        direct = kwargs.get("direct", True)
        user = kwargs.get("user")
        Document = apps.get_model("documents", "Document")
        folders = self.filter(parent=folder)
        documents = Document.objects.filter(folder=folder)
        if user:
            folders = folders.for_user(user)
            documents = documents.for_user(user)
        M = sorted(itertools.chain(folders, documents), key=operator.attrgetter("name"))
        if direct:
            return M
        for child in folders:
            M.extend(self.members(child, **kwargs))
        return M


class FolderQuerySet(QuerySet):

    def for_user(self, user):
        """
        All folders the given user can do something with.
        """
        qs = SharedMemberQuerySet(model=self.model, using=self._db, user=user)
        qs = qs.filter(Q(author=user) | Q(foldershareduser__user=user))
        return qs.distinct() & self.distinct()


class DocumentQuerySet(QuerySet):

    def for_user(self, user):
        """
        All documents the given user can do something with.
        """
        qs = SharedMemberQuerySet(model=self.model, using=self._db, user=user)
        qs = qs.filter(Q(author=user) | Q(documentshareduser__user=user))
        return qs.distinct() & self.distinct()


class SharedMemberQuerySet(QuerySet):

    def __init__(self, **kwargs):
        if "user" in kwargs:
            self.user = kwargs.pop("user")
        super().__init__(**kwargs)

    def iterator(self):
        shared_user_model = self.model.shared_user_model()
        shared_members = shared_user_model.for_user(self.user)
        for obj in super().iterator():
            if obj.pk in shared_members:
                obj._shared = True
            yield obj

    def _chain(self, **kwargs):
        kwargs["user"] = self.user
        return super()._chain(**kwargs)