from django.contrib.postgres.fields import ArrayField
from django.db import models
from django.urls import reverse
from django.utils.translation import gettext_lazy as _

from netbox.models.features import CloningMixin
from utilities.querysets import RestrictedQuerySet

__all__ = (
    'ObjectPermission',
)


class ObjectPermission(CloningMixin, models.Model):
    """
    A mapping of view, add, change, and/or delete permission for users and/or groups to an arbitrary set of objects
    identified by ORM query parameters.
    """
    name = models.CharField(
        verbose_name=_('name'),
        max_length=100
    )
    description = models.CharField(
        verbose_name=_('description'),
        max_length=200,
        blank=True
    )
    enabled = models.BooleanField(
        verbose_name=_('enabled'),
        default=True
    )
    object_types = models.ManyToManyField(
        to='contenttypes.ContentType',
        related_name='object_permissions'
    )
    actions = ArrayField(
        base_field=models.CharField(max_length=30),
        help_text=_("The list of actions granted by this permission")
    )
    constraints = models.JSONField(
        blank=True,
        null=True,
        verbose_name=_('constraints'),
        help_text=_("Queryset filter matching the applicable objects of the selected type(s)")
    )

    clone_fields = (
        'description', 'enabled', 'object_types', 'actions', 'constraints',
    )

    objects = RestrictedQuerySet.as_manager()

    class Meta:
        ordering = ['name']
        verbose_name = _('permission')
        verbose_name_plural = _('permissions')

    def __str__(self):
        return self.name

    @property
    def can_view(self):
        return 'view' in self.actions

    @property
    def can_add(self):
        return 'add' in self.actions

    @property
    def can_change(self):
        return 'change' in self.actions

    @property
    def can_delete(self):
        return 'delete' in self.actions

    def list_constraints(self):
        """
        Return all constraint sets as a list (even if only a single set is defined).
        """
        if type(self.constraints) is not list:
            return [self.constraints]
        return self.constraints

    def get_absolute_url(self):
        return reverse('users:objectpermission', args=[self.pk])
