from decimal import Decimal

from django.core.exceptions import ValidationError
from django.db import models
from django.utils import timezone


class SecondaryComputationPolicy(models.Model):
    class Level(models.TextChoices):
        LOWER_SECONDARY = "LOWER_SECONDARY", "Lower Secondary (S1-S4)"
        UPPER_SECONDARY = "UPPER_SECONDARY", "Upper Secondary (S5-S6 / A-Level)"

    class RoundingMode(models.TextChoices):
        ONE_DECIMAL = "ONE_DECIMAL", "One decimal place (69.4)"
        WHOLE_NUMBER = "WHOLE_NUMBER", "Whole number (half up)"
        WHOLE_NUMBER_DOWN = "WHOLE_NUMBER_DOWN", "Whole number (floor)"

    name = models.CharField(max_length=100, default="Uganda CBC Lower Secondary")
    section = models.ForeignKey("app.Section", on_delete=models.CASCADE, related_name="secondary_policies")
    level = models.CharField(max_length=30, choices=Level.choices, default=Level.LOWER_SECONDARY)
    ca_weight = models.DecimalField(max_digits=5, decimal_places=2, default=Decimal("20.00"))
    exam_weight = models.DecimalField(max_digits=5, decimal_places=2, default=Decimal("80.00"))
    rounding_mode = models.CharField(
        max_length=30,
        choices=RoundingMode.choices,
        default=RoundingMode.ONE_DECIMAL,
    )
    effective_from = models.DateField(default=timezone.now)
    effective_to = models.DateField(blank=True, null=True)
    is_active = models.BooleanField(default=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ["-effective_from", "-id"]
        db_table = "app_secondarycomputationpolicy"

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

    def clean(self):
        total = (self.ca_weight or Decimal("0")) + (self.exam_weight or Decimal("0"))
        if total != Decimal("100.00"):
            raise ValidationError("CA weight and exam weight must total 100%.")
        if self.effective_to and self.effective_to < self.effective_from:
            raise ValidationError("Effective-to date cannot be before effective-from date.")

    @classmethod
    def get_active_policy(cls, section=None, level=Level.LOWER_SECONDARY, on_date=None):
        on_date = on_date or timezone.now().date()
        queryset = cls.objects.filter(
            is_active=True,
            level=level,
            effective_from__lte=on_date,
        ).filter(models.Q(effective_to__isnull=True) | models.Q(effective_to__gte=on_date))
        if section is not None:
            queryset = queryset.filter(section=section)
        return queryset.order_by("-effective_from", "-id").first()


class SecondaryGradeBand(models.Model):
    policy = models.ForeignKey(
        SecondaryComputationPolicy,
        on_delete=models.CASCADE,
        related_name="grade_bands",
    )
    grade = models.CharField(max_length=5)
    descriptor = models.CharField(max_length=120)
    min_score = models.DecimalField(max_digits=5, decimal_places=2)
    max_score = models.DecimalField(max_digits=5, decimal_places=2)
    display_order = models.PositiveSmallIntegerField(default=0)

    class Meta:
        unique_together = ("policy", "grade")
        ordering = ["display_order", "-min_score"]
        db_table = "app_secondarygradeband"

    def __str__(self):
        return f"{self.grade} ({self.min_score}-{self.max_score})"

    def clean(self):
        if self.min_score > self.max_score:
            raise ValidationError("Minimum score cannot be greater than maximum score.")
