from django.db import models
from mptt.models import MPTTModel, TreeForeignKey
from django.contrib.auth.models import User
from abschnitte.models import Textabschnitt
from stichworte.models import Stichwort
from referenzen.models import Referenz
from rollen.models import Rolle
import datetime

class Dokumententyp(models.Model):
    name = models.CharField(max_length=100, primary_key=True)
    verantwortliche_ve = models.CharField(max_length=255)

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

    class Meta:
        verbose_name="Dokumententyp"
        verbose_name_plural="Dokumententypen"


class Person(models.Model):
    name = models.CharField(max_length=100, primary_key=True)
    funktion = models.CharField(max_length=255)

    def __str__(self):
        return self.name
    class Meta:
        verbose_name_plural="Personen"
        ordering = ['name']
        verbose_name="Person"

class Thema(models.Model):
    name = models.CharField(max_length=100, primary_key=True)
    erklaerung = models.TextField(blank=True)

    def __str__(self):
        return self.name
    class Meta:
        verbose_name_plural="Themen"
        verbose_name="Thema"

class Dokument(models.Model):
    nummer = models.CharField(max_length=50, primary_key=True)
    dokumententyp = models.ForeignKey(Dokumententyp, on_delete=models.PROTECT)
    name = models.CharField(max_length=255)
    autoren = models.ManyToManyField(Person, related_name='verfasste_dokumente')
    pruefende = models.ManyToManyField(Person, related_name='gepruefte_dokumente')
    gueltigkeit_von = models.DateField(null=True, blank=True)
    gueltigkeit_bis = models.DateField(null=True, blank=True)
    signatur_cso = models.CharField(max_length=255, blank=True)
    anhaenge = models.TextField(blank=True)
    aktiv = models.BooleanField(blank=True,default=False)

    def __str__(self):
        return f"{self.nummer} – {self.name}"

    @property
    def dates(self):
        """
        Returns an array of unique, chronologically sorted dates representing
        state-change dates from all Vorgaben in this document.
        
        These are dates where Vorgaben become active (gueltigkeit_von) or change state
        (the day after gueltigkeit_bis). The very last date in the list is excluded
        as it has no relevance (nothing changes after it).
        """
        dates_set = set()
        
        # Get all vorgaben for this document
        for vorgabe in self.vorgaben.all():
            # Add gueltigkeit_von (when vorgabe becomes active)
            if vorgabe.gueltigkeit_von:
                dates_set.add(vorgabe.gueltigkeit_von)
            
            # Add the day after gueltigkeit_bis (when vorgabe expires/changes state)
            # Only if gueltigkeit_bis is defined (not None)
            if vorgabe.gueltigkeit_bis:
                dates_set.add(vorgabe.gueltigkeit_bis + datetime.timedelta(days=1))
        
        # Return sorted unique dates from oldest to newest, excluding the last date
        # (but only if there are multiple dates; single dates are kept)
        sorted_dates = sorted(list(dates_set))
        return sorted_dates[:-1] if len(sorted_dates) > 1 else sorted_dates

    class Meta:
        verbose_name_plural="Dokumente"
        verbose_name="Dokument"

class Vorgabe(models.Model):
    order = models.IntegerField()
    nummer = models.IntegerField()
    dokument = models.ForeignKey(Dokument, on_delete=models.CASCADE, related_name='vorgaben')
    thema = models.ForeignKey(Thema, on_delete=models.PROTECT, blank=False)
    titel = models.CharField(max_length=255)
    referenzen = models.ManyToManyField(Referenz, blank=True)
    gueltigkeit_von = models.DateField()
    gueltigkeit_bis = models.DateField(blank=True,null=True)
    stichworte = models.ManyToManyField(Stichwort, blank=True)
    relevanz = models.ManyToManyField(Rolle,blank=True)

    def Vorgabennummer(self):
        return str(self.dokument.nummer)+"."+self.thema.name[0]+"."+str(self.nummer)

    def get_status(self, check_date: datetime.date = datetime.date.today(), verbose: bool = False) -> str:
        if self.gueltigkeit_von > check_date:
            return "future" if not verbose else "Ist erst ab dem "+self.gueltigkeit_von.strftime('%d.%m.%Y')+" in Kraft."

        if not self.gueltigkeit_bis:
            return "active"

        if self.gueltigkeit_bis >= check_date:
            return "active"

        return "expired" if not verbose else "Ist seit dem "+self.gueltigkeit_bis.strftime('%d.%m.%Y')+" nicht mehr in Kraft."

    def __str__(self):
        return f"{self.Vorgabennummer()}: {self.titel}"

    @staticmethod
    def sanity_check_vorgaben():
        """
        Sanity check for Vorgaben:
        If there are two Vorgaben with the same number, Thema and Dokument,
        their valid_from and valid_to date ranges shouldn't intersect.
        
        Returns:
            list: List of dictionaries containing conflicts found
        """
        conflicts = []
        
        # Group Vorgaben by dokument, thema, and nummer
        from django.db.models import Count
        from itertools import combinations
        
        # Find Vorgaben with same dokument, thema, and nummer
        duplicate_groups = (
            Vorgabe.objects.values('dokument', 'thema', 'nummer')
            .annotate(count=Count('id'))
            .filter(count__gt=1)
        )
        
        for group in duplicate_groups:
            # Get all Vorgaben in this group
            vorgaben = Vorgabe.objects.filter(
                dokument=group['dokument'],
                thema=group['thema'], 
                nummer=group['nummer']
            )
            
            # Check all pairs for date range intersections
            for vorgabe1, vorgabe2 in combinations(vorgaben, 2):
                if Vorgabe._date_ranges_intersect(
                    vorgabe1.gueltigkeit_von, vorgabe1.gueltigkeit_bis,
                    vorgabe2.gueltigkeit_von, vorgabe2.gueltigkeit_bis
                ):
                    conflicts.append({
                        'vorgabe1': vorgabe1,
                        'vorgabe2': vorgabe2,
                        'conflict_type': 'date_range_intersection',
                        'message': f"Vorgaben {vorgabe1.Vorgabennummer()} and {vorgabe2.Vorgabennummer()} "
                                  f"überschneiden sich in der Geltungsdauer"
                    })
        
        return conflicts

    def clean(self):
        """
        Validate the Vorgabe before saving.
        """
        from django.core.exceptions import ValidationError

        # Check that thema is provided
        if not self.thema_id:
            raise ValidationError({
                'thema': 'Thema ist ein Pflichtfeld. Bitte wählen Sie ein Thema aus.'
            })

        # Check for conflicts with existing Vorgaben
        conflicts = self.find_conflicts()
        if conflicts:
            conflict_messages = [c['message'] for c in conflicts]
            raise ValidationError({
                '__all__': conflict_messages
            })
    
    def find_conflicts(self):
        """
        Find conflicts with existing Vorgaben.
        
        Returns:
            list: List of conflict dictionaries
        """
        conflicts = []
        
        # Find Vorgaben with same dokument, thema, and nummer (excluding self)
        existing_vorgaben = Vorgabe.objects.filter(
            dokument=self.dokument,
            thema=self.thema,
            nummer=self.nummer
        ).exclude(pk=self.pk)
        
        for other_vorgabe in existing_vorgaben:
            if self._date_ranges_intersect(
                self.gueltigkeit_von, self.gueltigkeit_bis,
                other_vorgabe.gueltigkeit_von, other_vorgabe.gueltigkeit_bis
            ):
                conflicts.append({
                    'vorgabe1': self,
                    'vorgabe2': other_vorgabe,
                    'conflict_type': 'date_range_intersection',
                    'message': f"Vorgabe {self.Vorgabennummer()} in Konflikt mit "
                              f"bestehender {other_vorgabe.Vorgabennummer()} "
                              f" - Geltungsdauer übeschneidet sich"
                })
        
        return conflicts

    @staticmethod
    def _date_ranges_intersect(start1, end1, start2, end2):
        """
        Check if two date ranges intersect.
        None end date means open-ended range.
        
        Args:
            start1, start2: Start dates
            end1, end2: End dates (can be None for open-ended)
            
        Returns:
            bool: True if ranges intersect
        """
        # If either start date is None, treat it as invalid case
        if not start1 or not start2:
            return False
            
        # If end date is None, treat it as far future
        end1 = end1 or datetime.date.max
        end2 = end2 or datetime.date.max
        
        # Ranges intersect if start1 <= end2 and start2 <= end1
        return start1 <= end2 and start2 <= end1

    class Meta:
        verbose_name_plural="Vorgaben"
        ordering = ['order']

class VorgabeLangtext(Textabschnitt):
    abschnitt=models.ForeignKey(Vorgabe,on_delete=models.CASCADE)
    class Meta:
        verbose_name_plural="Langtext"
        verbose_name="Langtext-Abschnitt"

class VorgabeKurztext(Textabschnitt):
    abschnitt=models.ForeignKey(Vorgabe,on_delete=models.CASCADE)
    class Meta:
        verbose_name_plural="Kurztext"
        verbose_name="Kurztext-Abschnitt"

class Geltungsbereich(Textabschnitt):
    geltungsbereich=models.ForeignKey(Dokument,on_delete=models.CASCADE)
    class Meta:
        verbose_name_plural="Geltungsbereich"
        verbose_name="Geltungsbereichs-Abschnitt"

class Einleitung(Textabschnitt):
    einleitung=models.ForeignKey(Dokument,on_delete=models.CASCADE)
    class Meta:
        verbose_name_plural="Einleitung"
        verbose_name="Einleitungs-Abschnitt"

class Checklistenfrage(models.Model):
    vorgabe=models.ForeignKey(Vorgabe, on_delete=models.CASCADE, related_name="checklistenfragen")
    frage = models.CharField(max_length=255)

    def __str__(self):
        return self.frage

    class Meta:
        verbose_name_plural="Fragen für Checkliste"
        verbose_name="Frage für Checkliste"

class VorgabenTable(Vorgabe):
    class Meta:
        proxy = True
        verbose_name = "Vorgabe (Tabellenansicht)"
        verbose_name_plural = "Vorgaben (Tabellenansicht)"

class Changelog(models.Model):
    dokument = models.ForeignKey(Dokument, on_delete=models.CASCADE, related_name='changelog')
    autoren = models.ManyToManyField(Person)
    datum = models.DateField()
    aenderung = models.TextField()

    def __str__(self):
        return f"{self.datum} – {self.dokument.nummer}"

    class Meta:
        verbose_name_plural="Changelog"
        verbose_name="Changelog-Eintrag"


class VorgabeComment(models.Model):
    vorgabe = models.ForeignKey(Vorgabe, on_delete=models.CASCADE, related_name='comments')
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    text = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        verbose_name = "Vorgabe-Kommentar"
        verbose_name_plural = "Vorgabe-Kommentare"
        ordering = ['-created_at']

    def __str__(self):
        return f"Kommentar von {self.user.username} zu {self.vorgabe.Vorgabennummer()}"
