import string

from django.core.validators import RegexValidator
from django.db import models
from django.utils.crypto import get_random_string
from django.utils.translation import ugettext_lazy as _

from pretix.base.models.base import LoggedModel
from pretix.base.validators import OrganizerSlugBlacklistValidator

from ..settings import settings_hierarkey
from .auth import User


@settings_hierarkey.add(cache_namespace='organizer')
class Organizer(LoggedModel):
    """
    This model represents an entity organizing events, e.g. a company, institution,
    charity, person, …

    :param name: The organizer's name
    :type name: str
    :param slug: A globally unique, short name for this organizer, to be used
                 in URLs and similar places.
    :type slug: str
    """

    settings_namespace = 'organizer'
    name = models.CharField(max_length=200,
                            verbose_name=_("Name"))
    slug = models.SlugField(
        max_length=50, db_index=True,
        help_text=_(
            "Should be short, only contain lowercase letters and numbers, and must be unique among your events. "
            "This is being used in addresses and bank transfer references."),
        validators=[
            RegexValidator(
                regex="^[a-zA-Z0-9.-]+$",
                message=_("The slug may only contain letters, numbers, dots and dashes.")
            ),
            OrganizerSlugBlacklistValidator()
        ],
        verbose_name=_("Short form"),
    )
    permitted = models.ManyToManyField(User, through='OrganizerPermission',
                                       related_name="organizers")

    class Meta:
        verbose_name = _("Organizer")
        verbose_name_plural = _("Organizers")
        ordering = ("name",)

    def __str__(self) -> str:
        return self.name

    def save(self, *args, **kwargs):
        obj = super().save(*args, **kwargs)
        self.get_cache().clear()
        return obj

    def get_cache(self) -> "pretix.base.cache.ObjectRelatedCache":
        """
        Returns an :py:class:`ObjectRelatedCache` object. This behaves equivalent to
        Django's built-in cache backends, but puts you into an isolated environment for
        this organizer, so you don't have to prefix your cache keys. In addition, the cache
        is being cleared every time the organizer changes.
        """
        from pretix.base.cache import ObjectRelatedCache

        return ObjectRelatedCache(self)


def generate_invite_token():
    return get_random_string(length=32, allowed_chars=string.ascii_lowercase + string.digits)


class OrganizerPermission(models.Model):
    """
    The relation between an Organizer and a User who has permissions to
    access an organizer profile.

    :param organizer: The organizer this relation refers to
    :type organizer: Organizer
    :param user: The user this set of permissions is valid for
    :type user: User
    :param can_create_events: Whether or not this user can create new events with this
                              organizer account.
    :type can_create_events: bool
    """

    organizer = models.ForeignKey(Organizer, related_name="user_perms", on_delete=models.CASCADE)
    user = models.ForeignKey(User, related_name="organizer_perms", on_delete=models.CASCADE, null=True, blank=True)
    invite_email = models.EmailField(null=True, blank=True)
    invite_token = models.CharField(default=generate_invite_token, max_length=64, null=True, blank=True)
    can_create_events = models.BooleanField(
        default=True,
        verbose_name=_("Can create events"),
    )
    can_change_permissions = models.BooleanField(
        default=True,
        verbose_name=_("Can change permissions"),
    )

    class Meta:
        verbose_name = _("Organizer permission")
        verbose_name_plural = _("Organizer permissions")

    def __str__(self) -> str:
        return _("%(name)s on %(object)s") % {
            'name': str(self.user),
            'object': str(self.organizer),
        }
