# Provide permissions handling
# we don't import models at module level in order to avoid migration problems
from django.utils.translation import gettext_lazy as _
from django.contrib.auth.models import Group, Permission
from django.contrib.contenttypes.models import ContentType

from .models import Program


__all__ = ("PagePermissions", "program")


class PagePermissions:
    """Handles obj permissions initialization of page subclass."""

    model = None
    # TODO: move values to subclass
    groups = ({"label": _("editors"), "field": "editors_group_id", "perms": ["update"]},)
    """Groups informations initialized."""
    groups_name_format = "{obj.title}: {group_label}"
    """Format used for groups name."""
    perms_name_format = "{obj.title}: can {perm}"
    """Format used for permission name (displayed to humans)."""
    perms_codename_format = "{obj._meta.label_lower}_{obj.pk}_{perm}"
    """Format used for permissions codename."""
    perms_cn_format = "{obj._meta.model_name}_{obj.pk}_{perm}"

    def __init__(self, model):
        self.model = model

    def can(self, user, perm, obj):
        """Return True wether if user can edit Program or its children."""
        from .models.page import ChildPage

        if isinstance(obj, ChildPage):
            obj = obj.parent_subclass

        if not isinstance(obj, self.model):
            return False

        if user.is_superuser:
            return True

        perm = self.perms_codename_format.format(self=self, perm=perm, obj=obj)
        return user.has_perm(perm)

    def init(self, obj, model=None):
        """Initialize permissions for the provided obj.

        Return True if group  or permission have been created (`obj` has
        thus been saved).
        """
        updated = False
        created_groups = []

        # init groups
        for infos in self.groups:
            group = getattr(obj, infos["field"])
            if not group:
                group, created = self.init_group(obj, infos)
                setattr(obj, infos["field"], group.pk)
                updated = True
                created and created_groups.append((group, infos))

        if updated:
            obj.save()

        # init perms
        for group, infos in created_groups:
            self.init_perms(obj, group, infos)

        return updated

    def init_group(self, obj, infos):
        name = self.groups_name_format.format(obj=obj, group_label=infos["label"])
        return Group.objects.get_or_create(name=name)

    def init_perms(self, obj, group, infos):
        # TODO: avoid multiple database hits
        for name in infos["perms"]:
            perm, _ = Permission.objects.get_or_create(
                codename=self.perms_cn_format.format(obj=obj, perm=name),
                content_type=ContentType.objects.get_for_model(obj),
                defaults={"name": self.perms_name_format.format(obj=obj, perm=name)},
            )
            if perm not in group.permissions.all():
                group.permissions.add(perm)


program = PagePermissions(Program)
