from django.contrib.auth.models import Permission
from django.core.exceptions import PermissionDenied
from django.db import models


# Create your models here.
class Role(models.Model):
    name = models.CharField(max_length=100, unique=True)
    permissions = models.ManyToManyField(Permission)
    editable = models.BooleanField(default=True, help_text="Check if this role can be edited.")
    externally_provisioned = models.BooleanField(
        default=False,
        help_text="Check if this role was provisioned via a configuration file (yaml..).",
    )
    is_active = models.BooleanField(
        default=True,
        help_text="Inactive roles have their permissions suspended but retain their role bindings.",
    )

    def deactivate(self):
        """Deactivate this role, suspending all permissions granted through its bindings."""
        self.is_active = False
        self.save()
        for rb in self.rolebindings.all():
            rb._remove_all_perms_from_subject()

    def reactivate(self):
        """Reactivate this role, restoring all permissions through its bindings."""
        self.is_active = True
        self.save()
        for rb in self.rolebindings.all():
            rb._add_all_perms_to_subject()

    def _check_editable(self):
        if not self.is_active:
            raise PermissionDenied(f"{self.name} role is deactivated.")
        if not self.editable:
            raise PermissionDenied(f"{self.name} role cannot be edited.")

    def add_many_permissions(self, permissions):
        self._check_editable()
        new_perms = []
        for permission in permissions:
            if self.add_permission(permission):
                new_perms.append(permission.codename)
        return new_perms

    def add_permission(self, perm):
        self._check_editable()
        if perm not in self.permissions.all():
            self.permissions.add(perm)
            for role_bind in self.rolebindings.all():
                role_bind._add_single_perm_to_subject(perm)
            return True
        return False

    def remove_permission(self, perm):
        self._check_editable()
        self.permissions.remove(perm)
        for role_bind in self.rolebindings.all():
            role_bind._remove_single_perm_from_subject(perm)

    def delete(self, *args, **kwargs):
        if not self.editable:
            raise PermissionDenied(f"{self.name} role cannot be deleted.")

        self.permissions.clear()
        super().delete(*args, **kwargs)

    def set_immutable(self):
        self.editable = False

    def set_editable(self):
        self.editable = True

    def __str__(self):
        return self.name
