""" Models for buid menus """

# Django
from django.db import models
from django.urls import reverse
from django.urls.exceptions import NoReverseMatch
from django.utils.functional import cached_property
from django.utils.text import slugify
from django.apps import apps
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType
from django.conf import settings

# Local
from . import site
from .utils import import_class


class Action(models.Model):
    class ToChoices(models.IntegerChoices):
        MODEL = 1, "Modelo"
        CLASSVIEW = 2, "Vista"

    to = models.PositiveSmallIntegerField(
        choices=ToChoices.choices, verbose_name="acción hacia un"
    )
    app_label = models.CharField(max_length=128, verbose_name="aplicación")
    name = models.CharField(max_length=128, verbose_name="nombre de la acción")
    element = models.CharField(max_length=128, verbose_name="elemento accionado")
    permissions = models.ManyToManyField(
        Permission, blank=True, verbose_name="permisos específicos"
    )

    def __str__(self):
        return self.name

    class Meta:
        verbose_name = "acción"
        verbose_name_plural = "acciones"
        unique_together = ("app_label", "element")
        ordering = ("name",)

    @property
    def model(self):
        if self.to != self.ToChoices.MODEL:
            return None
        try:
            model_class = apps.get_model(self.app_label, self.element)
            return model_class
        except LookupError:
            return None

    @property
    def view(self):
        if self.to != self.ToChoices.CLASSVIEW:
            return None
        try:
            app_config = apps.get_app_config(self.app_label)
            view_class = import_class(f"{app_config.name}.views", self.element)
            return view_class
        except (LookupError):
            return None

    def get_permissions(self):
        return [
            f"{perm.content_type.app_label}.{perm.codename}"
            for perm in self.cached_permissions
        ]

    @cached_property
    def cached_permissions(self):
        return list(self.permissions.select_related("content_type").all())

    def has_permissions(self, user, user_perms=None):
        if not user.is_authenticated or not user.is_active:
            return False

        if user.is_superuser:
            return True

        if user_perms is None:
            user_perms = set(user.get_all_permissions())

        if self.to == self.ToChoices.MODEL:
            basic_perms = any(
                f"{self.app_label}.{perm}_{self.element}" in user_perms
                for perm in ("view", "add", "change", "delete")
            )
        else:
            basic_perms = True

        specific_perms = all(p in user_perms for p in self.get_permissions())
        return basic_perms and specific_perms


class Menu(models.Model):
    """Models for menu"""

    parent = models.ForeignKey(
        "self",
        blank=True,
        null=True,
        related_name="submenus",
        on_delete=models.CASCADE,
        verbose_name="menú padre",
    )
    name = models.CharField(max_length=128, verbose_name="nombre")
    route = models.CharField(
        max_length=512, unique=True, editable=False, verbose_name="ruta de acceso"
    )
    action = models.ForeignKey(Action, on_delete=models.CASCADE, verbose_name="acción")
    icon_class = models.CharField(
        max_length=128, blank=True, null=True, verbose_name="clase css del ícono"
    )
    is_group = models.BooleanField(default=False, editable=False, verbose_name="agrupa")
    sequence = models.PositiveSmallIntegerField(verbose_name="secuencia")
    is_active = models.BooleanField(default=True, verbose_name="activo?")

    class Meta:
        ordering = ("sequence",)

    def __str__(self):
        return f"{self.name} | {self.get_route()}"

    def get_route(self):
        route = (
            f"{self.parent.get_route()}/{slugify(self.name)}"
            if self.parent
            else slugify(self.name)
        )
        return route

    def get_url(self):
        url_name = None
        reverse_url = True
        if self.action.to == Action.ToChoices.MODEL:
            model_class = self.action.model
            if model_class and site.is_registered(model_class):
                model_site = site.get_modelsite(model_class)
                url_name = model_site.get_url_name("list")
        else:
            # url_name = f"site:{slugify(self.name)}"
            url_name = f"/{self.route}"
            reverse_url = False
        try:
            if reverse_url:
                url_name = reverse(url_name)
            return url_name
        except NoReverseMatch:
            if settings.DEBUG and url_name:
                print("DEBUG: Not found url for %s" % url_name)
        return "/page/not-found/"
