from django.contrib.auth import get_user_model
from django.db import models
from martor.models import MartorField

from territories_dashboard_lib.commons.models import CommonModel
from territories_dashboard_lib.indicators_lib.enums import (
    AggregationFunctions,
    MeshLevel,
)
from territories_dashboard_lib.indicators_lib.refresh_filters import refresh_filters


class Theme(CommonModel):
    ordering = models.IntegerField(
        default=0,
        verbose_name="Ordre dans la sidebar",
        help_text="L'ordre démarre à 0.",
    )
    name = models.CharField(
        max_length=64,
        unique=True,
        verbose_name="Nom (~id, URL)",
        help_text="Pour l'URL, ne mettre que des lettres minuscules sans accents et des tirets.",
    )
    title = models.CharField(max_length=128, verbose_name="Titre (affiché)")
    objectif_theme = models.TextField(blank=True, verbose_name="Objectif")
    action_theme = models.TextField(blank=True, verbose_name="Actions")

    def is_displayed_on_app(self):
        return self.sub_themes.all().exists()

    is_displayed_on_app.__name__ = "Visible ?"
    is_displayed_on_app.boolean = True

    def subthemes_count(self):
        return self.sub_themes.count()

    subthemes_count.__name__ = "Nb sous-thèmes"

    def indicators_count(self):
        return sum(
            [sub_theme.indicators_count() for sub_theme in self.sub_themes.all()]
        )

    indicators_count.__name__ = "Total indicateurs"

    def __str__(self):
        return self.title

    class Meta:
        ordering = ("ordering",)
        verbose_name = "1 - Thème"
        indexes = [
            models.Index(fields=["name"]),
        ]


class SubTheme(CommonModel):
    name = models.CharField(
        max_length=64,
        unique=True,
        verbose_name="Nom (~id, URL)",
        help_text="Pour l'URL, ne mettre que des lettres minuscules sans accents et des tirets.",
    )
    theme = models.ForeignKey(
        Theme, on_delete=models.CASCADE, related_name="sub_themes", verbose_name="Thème"
    )
    index_in_theme = models.IntegerField(
        default=0, verbose_name="Ordre thème", help_text="L'ordre démarre à 0."
    )
    title = models.CharField(max_length=128, verbose_name="Titre (affiché)")

    def __str__(self):
        return f"{self.theme.title} > {self.title}"

    def indicators_count(self):
        return self.indicators.count()

    def is_displayed_on_app(self):
        return self.indicators.all().exists()

    indicators_count.__name__ = "Nb indicateurs"
    is_displayed_on_app.__name__ = "Visible ?"
    is_displayed_on_app.boolean = True

    class Meta:
        ordering = ("theme", "index_in_theme")
        verbose_name = "2 - Sous-thème"
        indexes = [
            models.Index(fields=["theme", "index_in_theme"]),
            models.Index(fields=["name"]),
        ]


class Indicator(CommonModel):
    is_active = models.BooleanField(default=True, verbose_name="Actif ?")
    hidden = models.BooleanField(
        default=False,
        verbose_name="Caché",
        help_text="Coché pour cacher un indicateur actif, il pourra être utilisé en tant que second indicateur sur la carte mais il n'apparaitra pas dans la liste des indicateurs",
    )
    sub_theme = models.ForeignKey(
        SubTheme,
        on_delete=models.CASCADE,
        related_name="indicators",
        verbose_name="Sous-thème",
    )
    index_in_theme = models.IntegerField(
        default=0, verbose_name="Ordre sous-thème", help_text="L'ordre démarre à 0."
    )
    name = models.CharField(
        max_length=32,
        unique=True,
        verbose_name="Nom (~id, URL)",
        help_text="Pour l'URL, ne mettre que des lettres minuscules sans accents et des tirets.",
    )
    title = models.CharField(max_length=128, verbose_name="Titre (affiché)")
    short_title = models.CharField(
        max_length=128,
        null=True,
        blank=True,
        verbose_name="Titre abrégé",
        help_text="Utilisé pour les popups et la légende de la carte.",
    )
    # Indicator's DB attributes
    db_table_prefix = models.CharField(
        max_length=128,
        verbose_name="Préfixe dans la DB",
        help_text="Attention, le nom doit être exactement le même que celui en base.",
    )
    min_mesh = models.TextField(choices=MeshLevel.choices, default=MeshLevel.com)
    is_composite = models.BooleanField(
        default=False, verbose_name="Indicateur composite"
    )
    show_alternative = models.BooleanField(
        default=True,
        verbose_name="Afficher la valeur alternative",
        help_text="Pour certains indicateurs, il n'y a pas de valeur alternative à afficher.",
    )
    aggregation_constant = models.DecimalField(
        default=1,
        decimal_places=5,
        max_digits=10,
        verbose_name="Constante d'agrégation",
    )
    aggregation_function = models.CharField(
        default=AggregationFunctions.REPEATED_COMPONENT_2,
        max_length=8,
        choices=AggregationFunctions.choices,
        verbose_name="Fonction d'agrégation",
    )
    unite = models.CharField(max_length=32, verbose_name="Unité (affichée)")
    unite_nom_accessible = models.CharField(
        max_length=64,
        default="",
        blank=True,
        help_text="Nom accessible de l'unité qui sera lu par le lecteur d'écran.",
    )
    unite_alternative = models.CharField(
        default=None,
        null=True,
        blank=True,
        max_length=32,
        verbose_name="Unité alternative (affichée)",
    )
    unite_alternative_nom_accessible = models.CharField(
        max_length=64,
        default="",
        blank=True,
        help_text="Nom accessible de l'unité alternative qui sera lu par le lecteur d'écran.",
    )
    secondary_indicator = models.ForeignKey(
        "indicators_lib.Indicator",
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        verbose_name="Second indicateur à afficher sur la carte",
        help_text="Optionnel.",
    )
    flows_db_table_prefix = models.CharField(
        null=True,
        blank=True,
        max_length=128,
        verbose_name="Table des flux (prefix)",
        help_text="Uniquement pour les indicateurs de flux. Nom de la table des flux.",
    )
    flows_dimension = models.CharField(
        null=True,
        blank=True,
        max_length=32,
        verbose_name="Dimension des flux",
        help_text="Uniquement pour les indicateurs de flux. Nom de la colonne de dimension dans la table des flux.",
    )
    # Descriptive attributes
    show_evolution = models.BooleanField(
        default=True, verbose_name="Activer l'historique"
    )
    source = models.TextField(default="", blank=True)
    description = models.TextField(default="", blank=True)
    methodo = MartorField(
        default="", blank=True, verbose_name="Méthodologie (markdown)"
    )
    methodo_file = models.BinaryField(null=True, blank=True)
    favorited_by = models.ManyToManyField(
        get_user_model(),
        through="UserFavoriteIndicator",
        related_name="favorite_indicators",
    )

    def __str__(self):
        return self.title

    def get_theme_title(self):
        return self.sub_theme.theme.title

    get_theme_title.short_description = "Thème"

    def table_name(indicator, mesh: MeshLevel, *, flows: bool = False):
        table_prefix = (
            indicator.flows_db_table_prefix if flows else indicator.db_table_prefix
        )
        return f"{table_prefix}_{mesh}"

    class Meta:
        ordering = ("sub_theme", "index_in_theme")
        verbose_name = "3 - Indicateur"
        indexes = [
            models.Index(fields=["sub_theme"]),
            models.Index(fields=["name"]),
        ]


class UserFavoriteIndicator(models.Model):
    user = models.ForeignKey(
        get_user_model(),
        on_delete=models.CASCADE,
        related_name="favorite_indicator_links",
    )
    indicator = models.ForeignKey(
        Indicator,
        on_delete=models.CASCADE,
        related_name="favorite_links",
    )
    order = models.PositiveIntegerField(default=0)

    class Meta:
        unique_together = ("user", "indicator")
        ordering = ("user", "order")


class Dimension(CommonModel):
    indicator = models.ForeignKey(
        Indicator, on_delete=models.CASCADE, related_name="dimensions"
    )
    db_name = models.TextField()
    title = models.TextField()
    is_breakdown = models.BooleanField(
        default=False,
        verbose_name="Répartir selon cette dimension",
        help_text="Dans le cas de plusieurs dimensions pour un indicateur, l'une d'entre elles doit être la dimension de réparition pour les graphiques.",
    )

    def save(self, *args, **kwargs):
        is_new = self.pk is None
        result = super().save(*args, **kwargs)
        if is_new:
            try:
                refresh_filters(self)
            except Exception as e:
                print(e)
        return result


class Filter(CommonModel):
    dimension = models.ForeignKey(
        Dimension, related_name="filters", on_delete=models.CASCADE
    )
    db_name = models.CharField(max_length=128, verbose_name="Nom dans la BDD")
    order = models.IntegerField(
        default=0, verbose_name="Ordre", help_text="L'ordre démarre à 0."
    )
    default = models.BooleanField(default=True, verbose_name="Sélectionné par défaut ?")
    color = models.TextField(
        null=True,
        blank=True,
        verbose_name="Couleur",
        help_text="format hexadécimale : #FF11DD. Les données associées à ce filtre apparaitront de cette couleur dans les graphiques.",
    )

    def __str__(self):
        return self.db_name

    class Meta:
        ordering = ("dimension", "order", "db_name")
        verbose_name = "Filtre"
        indexes = [
            models.Index(fields=["db_name"]),
            models.Index(fields=["dimension", "order"]),
        ]
        unique_together = ("dimension", "db_name")
