from decimal import Decimal
from django.conf import settings
from django.core.exceptions import ValidationError
from django.db import models

class School(models.Model):
    SCHOOL_TYPES = [
        ("HSS", "HSS"),
        ("HS", "HS"),
        ("UP", "UP"),
        ("LP", "LP"),
    ]
    name = models.CharField(max_length=200, unique=True)
    code = models.CharField(max_length=40, unique=True)
    school_type = models.CharField(max_length=3, choices=SCHOOL_TYPES)
    address = models.TextField(blank=True)
    phone = models.CharField(max_length=30, blank=True)
    email = models.EmailField(blank=True)
    principal_name = models.CharField(max_length=150, blank=True)
    hm_name = models.CharField(max_length=150, blank=True)
    escort1_name = models.CharField(max_length=150, blank=True)
    escort1_mobile = models.CharField(max_length=30, blank=True)
    escort2_name = models.CharField(max_length=150, blank=True)
    escort2_mobile = models.CharField(max_length=30, blank=True)
    confirmed = models.BooleanField(default=False)
    confirmed_at = models.DateTimeField(null=True, blank=True)

    class Meta:
        ordering = ["code"]

    def __str__(self):
        return f"{self.code} - {self.name}"

class AcademicYear(models.Model):
    name = models.CharField(max_length=30, unique=True)
    active = models.BooleanField(default=True)

    class Meta:
        ordering = ["-name"]

    def __str__(self):
        return self.name

class SystemSetting(models.Model):
    key = models.CharField(max_length=80, unique=True)
    value = models.CharField(max_length=200, blank=True)

    def __str__(self):
        return self.key

    @classmethod
    def get_int(cls, key, default):
        obj = cls.objects.filter(key=key).first()
        try:
            return int(obj.value) if obj else default
        except (TypeError, ValueError):
            return default

class Item(models.Model):
    SEX = [("M", "Male"), ("F", "Female"), ("X", "Mixed")]
    PARTICIPATION = [("I", "Individual"), ("G", "Group")]
    SECTIONS = [("LP", "LP"), ("UP", "UP"), ("HS", "HS"), ("HSS", "HSS")]

    code = models.CharField(max_length=30, unique=True)
    name = models.CharField(max_length=200)
    section = models.CharField(max_length=3, choices=SECTIONS)
    sex = models.CharField(max_length=1, choices=SEX)
    participation_type = models.CharField(max_length=1, choices=PARTICIPATION)
    min_participants = models.PositiveIntegerField(default=1)
    max_participants = models.PositiveIntegerField(default=1)
    active = models.BooleanField(default=True)

    def clean(self):
        if self.min_participants > self.max_participants:
            raise ValidationError("Minimum participants cannot exceed maximum participants.")
        if self.participation_type == "I":
            self.min_participants = 1
            self.max_participants = 1

    def __str__(self):
        return f"{self.code} - {self.name}"

class Participant(models.Model):
    SEX = [("M", "Male"), ("F", "Female")]
    SECTIONS = [("LP", "LP"), ("UP", "UP"), ("HS", "HS"), ("HSS", "HSS")]

    school = models.ForeignKey(School, on_delete=models.CASCADE, related_name="participants")
    admission_no = models.CharField(max_length=50)
    name = models.CharField(max_length=200)
    sex = models.CharField(max_length=1, choices=SEX)
    age = models.PositiveIntegerField()
    section = models.CharField(max_length=3, choices=SECTIONS)
    class_name = models.CharField(max_length=30)
    chest_no = models.PositiveIntegerField(null=True, blank=True)

    class Meta:
        ordering = ["section", "sex", "name"]
        constraints = [
            models.UniqueConstraint(fields=["school", "admission_no"], name="unique_school_admission")
        ]

    def __str__(self):
        return f"{self.name} - {self.school.code}"

class ParticipantItem(models.Model):
    participant = models.ForeignKey(Participant, on_delete=models.CASCADE, related_name="item_links")
    item = models.ForeignKey(Item, on_delete=models.CASCADE, related_name="participant_links")

    class Meta:
        constraints = [
            models.UniqueConstraint(fields=["participant", "item"], name="unique_participant_item")
        ]

    def clean(self):
        if self.item.section != self.participant.section:
            raise ValidationError("Item section must match participant section.")
        if self.item.sex not in {"X", self.participant.sex}:
            raise ValidationError("Item sex does not match participant sex.")
        if self.participant.item_links.exclude(pk=self.pk).count() >= 5:
            raise ValidationError("A participant cannot have more than 5 items.")

class GroupEntry(models.Model):
    school = models.ForeignKey(School, on_delete=models.CASCADE, related_name="group_entries")
    item = models.ForeignKey(Item, on_delete=models.CASCADE, related_name="group_entries")
    captain = models.ForeignKey(Participant, on_delete=models.PROTECT, related_name="captain_entries")
    confirmed = models.BooleanField(default=False)

    class Meta:
        constraints = [
            models.UniqueConstraint(fields=["school", "item"], name="one_group_entry_per_school_item")
        ]

    def clean(self):
        if self.item.participation_type != "G":
            raise ValidationError("GroupEntry is only for group items.")
        if self.captain.school_id != self.school_id:
            raise ValidationError("Captain must belong to the same school.")

class GroupMember(models.Model):
    entry = models.ForeignKey(GroupEntry, on_delete=models.CASCADE, related_name="members")
    participant = models.ForeignKey(Participant, on_delete=models.PROTECT)

    class Meta:
        constraints = [
            models.UniqueConstraint(fields=["entry", "participant"], name="unique_group_member")
        ]

    def clean(self):
        if self.participant.school_id != self.entry.school_id:
            raise ValidationError("Group member must belong to the same school.")

class PointRule(models.Model):
    item = models.OneToOneField(Item, on_delete=models.CASCADE, related_name="point_rule")
    first = models.PositiveIntegerField(default=5)
    second = models.PositiveIntegerField(default=3)
    third = models.PositiveIntegerField(default=1)

class Result(models.Model):
    item = models.ForeignKey(Item, on_delete=models.CASCADE, related_name="results")
    participant = models.ForeignKey(
        Participant, on_delete=models.CASCADE, null=True, blank=True, related_name="results"
    )
    group_entry = models.ForeignKey(
        GroupEntry, on_delete=models.CASCADE, null=True, blank=True, related_name="results"
    )
    mark1 = models.DecimalField(max_digits=8, decimal_places=2, default=Decimal("0"))
    mark2 = models.DecimalField(max_digits=8, decimal_places=2, default=Decimal("0"))
    mark3 = models.DecimalField(max_digits=8, decimal_places=2, default=Decimal("0"))
    total = models.DecimalField(max_digits=10, decimal_places=2, default=Decimal("0"))
    position = models.PositiveIntegerField(null=True, blank=True)
    place = models.CharField(max_length=10, blank=True)
    confirmed = models.BooleanField(default=False)
    published = models.BooleanField(default=False)

    class Meta:
        constraints = [
            models.UniqueConstraint(
                fields=["item", "participant"],
                condition=models.Q(participant__isnull=False),
                name="unique_item_participant_result",
            ),
            models.UniqueConstraint(
                fields=["item", "group_entry"],
                condition=models.Q(group_entry__isnull=False),
                name="unique_item_group_result",
            ),
        ]

    @property
    def competitor_name(self):
        if self.participant:
            return self.participant.name
        if self.group_entry:
            return f"{self.group_entry.item.name} - {self.group_entry.school.name}"
        return "-"

    @property
    def competitor_school(self):
        if self.participant:
            return self.participant.school
        if self.group_entry:
            return self.group_entry.school
        return None

class ParticipantPoint(models.Model):
    participant = models.ForeignKey(Participant, on_delete=models.CASCADE, related_name="point_rows")
    result = models.ForeignKey(Result, on_delete=models.CASCADE, related_name="participant_point_rows")
    points = models.PositiveIntegerField(default=0)

class SchoolPoint(models.Model):
    school = models.ForeignKey(School, on_delete=models.CASCADE, related_name="point_rows")
    result = models.ForeignKey(Result, on_delete=models.CASCADE, related_name="school_point_rows")
    points = models.PositiveIntegerField(default=0)

class StageMark(models.Model):
    item = models.ForeignKey(Item, on_delete=models.CASCADE, related_name="stage_marks")
    participant = models.ForeignKey(Participant, on_delete=models.CASCADE, null=True, blank=True)
    group_entry = models.ForeignKey(GroupEntry, on_delete=models.CASCADE, null=True, blank=True)
    judge1 = models.DecimalField(max_digits=8, decimal_places=2, default=0)
    judge2 = models.DecimalField(max_digits=8, decimal_places=2, default=0)
    judge3 = models.DecimalField(max_digits=8, decimal_places=2, default=0)
    total = models.DecimalField(max_digits=8, decimal_places=2, default=0)
    place = models.PositiveIntegerField(null=True, blank=True)
    remark = models.CharField(max_length=250, blank=True)
    updated_at = models.DateTimeField(auto_now=True)

    def clean(self):
        if self.participant_id and self.group_entry_id:
            raise ValidationError("Stage mark cannot have both participant and group entry.")
