# Django
from django.conf import settings
from django.contrib.sites.models import Site
from django.db import models
from django.utils.functional import cached_property
from django.utils.html import escapejs
from django.utils.translation import gettext_lazy as _
from django.utils.translation import pgettext_lazy

# Third party
from parler.models import TranslatableModel, TranslatedFields


class Purpose(TranslatableModel):
    translations = TranslatedFields(
        name=models.CharField(verbose_name=_("Name"), max_length=100)
    )

    order = models.IntegerField(verbose_name=_("Order"), default=0)

    class Meta:
        verbose_name = _("Purpose")
        verbose_name_plural = _("Purposes")
        ordering = ["order"]

    def __str__(self):
        return self.name


class Application(TranslatableModel):
    translations = TranslatedFields(
        name=models.CharField(verbose_name=_("Name"), max_length=100),
        description=models.CharField(verbose_name=_("Description"), max_length=500),
    )
    slug = models.SlugField(verbose_name=_("Slug"), max_length=100)
    purposes = models.ManyToManyField(Purpose, verbose_name=_("Purposes"))
    no_consent = models.BooleanField(
        default=False,
        verbose_name=pgettext_lazy("for an application", "Do not need consent"),
        help_text=_(
            "The application will load even before the user gave explicit consent. Use only if the application does not collect user data."
        ),
    )
    is_required = models.BooleanField(
        default=False,
        verbose_name=pgettext_lazy("for an application", "Is required"),
        help_text=_(
            "Only if the application is required for the site to function correctly."
        ),
    )
    is_active = models.BooleanField(
        default=False, verbose_name=pgettext_lazy("for an application", "Is active")
    )
    order = models.IntegerField(verbose_name=_("Order"), default=0)

    class Meta:
        verbose_name = _("Application")
        verbose_name_plural = _("Applications")
        ordering = ["order"]

    @cached_property
    def camel_case_name(self):
        return "".join([s.capitalize() for s in self.slug.rsplit("-")])

    camel_case_name.short_description = _("Identifier")

    def __str__(self):
        return self.name


class Cookie(models.Model):
    application = models.ForeignKey(
        Application, verbose_name=_("Application"), on_delete=models.CASCADE
    )
    name = models.CharField(verbose_name=_("Name"), max_length=250)
    is_regexp = models.BooleanField(default=False, verbose_name=_("Regular expression"))
    path = models.CharField(verbose_name=_("Path"), max_length=250, blank=True)
    site = models.ForeignKey(Site, verbose_name=_("Site"), on_delete=models.CASCADE)

    class Meta:
        verbose_name = _("Cookie")
        verbose_name_plural = _("Cookies")

    def __str__(self):
        return self.name

    def save(self, *args, **kwargs):
        if self.path is None or self.path == "":
            self.path = "/"
        super().save(*args, **kwargs)

    @cached_property
    def prepared_name(self):
        domains = [self.site.domain]
        prepared_name = ""

        # Some cookie apply to domain without 'www' so we need to test all
        if self.site.domain.startswith("www"):
            domains.append(self.site.domain.split("www")[-1])

        if self.is_regexp:
            name = f"/{escapejs(self.name)}/"
        else:
            name = f"'{escapejs(self.name)}'"

        if self.path is not None:
            path = escapejs(self.path)
            loop_name = "[{},'{}','{}']"
        else:
            loop_name = "[{},'{}']"

        if settings.DEBUG:
            domains = ["localhost"]

        for i, domain in enumerate(domains):
            current_loop_name = loop_name
            if i < len(domains) - 1:
                current_loop_name += ", "
            domain = escapejs(domain)
            current_loop_name = current_loop_name.format(name, path, domain)
            prepared_name += current_loop_name
        return prepared_name


class DailyConsentStats(models.Model):
    """One entry per day with counters for each choice type."""

    date = models.DateField(
        unique=True,
        verbose_name=_("Date"),
    )
    accept_count = models.PositiveIntegerField(
        default=0,
        verbose_name=_("Accepts"),
    )
    decline_count = models.PositiveIntegerField(
        default=0,
        verbose_name=_("Declines"),
    )
    save_count = models.PositiveIntegerField(
        default=0,
        verbose_name=_("Saves (custom)"),
    )
    show_count = models.PositiveIntegerField(
        default=0,
        verbose_name=_("Banner displays (visitors)"),
    )
    no_interaction_count = models.PositiveIntegerField(
        default=0,
        verbose_name=_("No interaction (visitors)"),
    )
    # JSON: {"FR": 5, "US": 12} - country code (ISO 3166-1 alpha-2) -> visitor count
    country_counts = models.JSONField(
        default=dict,
        blank=True,
        verbose_name=_("Visitors by country"),
    )
    # JSON: {"organic": 42, "direct": 18, "paid": 5, "social": 8, "referral": 12}
    source_counts = models.JSONField(
        default=dict,
        blank=True,
        verbose_name=_("Visitors by traffic source"),
    )
    # JSON: {"google.com": 25, "facebook.com": 10} - referrer domain -> visitor count
    referral_counts = models.JSONField(
        default=dict,
        blank=True,
        verbose_name=_("Visitors by referral domain"),
    )

    class Meta:
        verbose_name = _("Daily consent stats")
        verbose_name_plural = _("Daily consent stats")
        ordering = ["-date"]

    def __str__(self):
        return str(self.date)

    @property
    def total(self):
        return self.accept_count + self.decline_count + self.save_count

    @property
    def acceptance_rate(self):
        total = self.total
        return (
            round(100 * (self.accept_count + self.save_count) / total) if total else 0
        )
