from decimal import Decimal

from django.contrib.auth.models import User
from django.core.exceptions import ValidationError
from django.db import models
from django.utils import timezone

from secondary.models.competencies import SubjectCompetency


class ContinuousAssessmentTask(models.Model):
    class TaskType(models.TextChoices):
        PROJECT = "PROJECT", "Project"
        PRACTICAL = "PRACTICAL", "Practical"
        COURSEWORK = "COURSEWORK", "Coursework"
        PORTFOLIO = "PORTFOLIO", "Portfolio"
        PRESENTATION = "PRESENTATION", "Presentation"
        GROUP_WORK = "GROUP_WORK", "Group Work"
        ORAL = "ORAL", "Oral Task"
        FIELD_STUDY = "FIELD_STUDY", "Field Study"
        OTHER = "OTHER", "Other"

    class Status(models.TextChoices):
        DRAFT = "DRAFT", "Draft"
        PENDING_MODERATION = "PENDING_MODERATION", "Pending Moderation"
        MODERATED = "MODERATED", "Moderated"
        APPROVED = "APPROVED", "Approved"
        LOCKED = "LOCKED", "Locked"

    title = models.CharField(max_length=150)
    academic_class = models.ForeignKey("app.AcademicClass", on_delete=models.CASCADE, related_name="ca_tasks")
    term = models.ForeignKey("app.Term", on_delete=models.CASCADE, related_name="ca_tasks")
    subject = models.ForeignKey("app.Subject", on_delete=models.CASCADE, related_name="ca_tasks")
    subject_competency = models.ForeignKey(
        SubjectCompetency,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="ca_tasks",
    )
    task_type = models.CharField(max_length=30, choices=TaskType.choices, default=TaskType.PROJECT)
    weight = models.DecimalField(max_digits=5, decimal_places=2, default=Decimal("0.00"))
    max_score = models.DecimalField(max_digits=6, decimal_places=2, default=Decimal("100.00"))
    assigned_date = models.DateField(default=timezone.now)
    evidence_required = models.BooleanField(default=False)
    uneb_eligible = models.BooleanField(default=True)
    status = models.CharField(max_length=25, choices=Status.choices, default=Status.DRAFT)
    created_by = models.ForeignKey(
        User,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="ca_tasks_created",
    )
    approved_by = models.ForeignKey(
        User,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="ca_tasks_approved",
    )
    approved_at = models.DateTimeField(null=True, blank=True)
    locked_at = models.DateTimeField(null=True, blank=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        unique_together = ("academic_class", "subject", "title", "assigned_date")
        ordering = ["-assigned_date", "-id"]
        db_table = "app_continuousassessmenttask"

    def __str__(self):
        return f"{self.title} - {self.subject}"

    def clean(self):
        if self.max_score <= 0:
            raise ValidationError("Maximum score must be greater than zero.")
        if self.weight < 0:
            raise ValidationError("Task weight cannot be negative.")
        if self.subject_id and self.academic_class_id:
            if self.subject.section_id != self.academic_class.section_id:
                raise ValidationError("Assessment task subject section must match class section.")
        if self.term_id and self.academic_class_id and self.term_id != self.academic_class.term_id:
            raise ValidationError("Task term must match the selected academic class term.")
        if self.subject_competency_id and self.subject_competency.subject_id != self.subject_id:
            raise ValidationError("Selected competency must belong to the selected subject.")

    def save(self, *args, **kwargs):
        if self.academic_class_id and not self.term_id:
            self.term = self.academic_class.term
        super().save(*args, **kwargs)


class ContinuousAssessmentRecord(models.Model):
    class ModerationStatus(models.TextChoices):
        DRAFT = "DRAFT", "Draft"
        PENDING = "PENDING", "Pending Moderation"
        MODERATED = "MODERATED", "Moderated"
        APPROVED = "APPROVED", "Approved"
        LOCKED = "LOCKED", "Locked"
        REJECTED = "REJECTED", "Rejected"

    task = models.ForeignKey(ContinuousAssessmentTask, on_delete=models.CASCADE, related_name="records")
    student = models.ForeignKey("app.Student", on_delete=models.CASCADE, related_name="ca_records")
    raw_score = models.DecimalField(max_digits=6, decimal_places=2)
    moderated_score = models.DecimalField(max_digits=6, decimal_places=2, null=True, blank=True)
    teacher_comment = models.TextField(blank=True, null=True)
    evidence_reference = models.CharField(max_length=255, blank=True, default="")
    moderation_status = models.CharField(
        max_length=20,
        choices=ModerationStatus.choices,
        default=ModerationStatus.DRAFT,
    )
    is_locked = models.BooleanField(default=False)
    entered_by = models.ForeignKey(
        User,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="ca_records_entered",
    )
    updated_by = models.ForeignKey(
        User,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="ca_records_updated",
    )
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        unique_together = ("task", "student")
        ordering = ["task_id", "student_id"]
        db_table = "app_continuousassessmentrecord"

    def __str__(self):
        return f"{self.student} - {self.task} ({self.raw_score})"

    def clean(self):
        max_score = self.task.max_score if self.task_id else Decimal("100.00")
        if self.raw_score < 0 or self.raw_score > max_score:
            raise ValidationError("Raw score must be between 0 and the task maximum score.")
        if self.moderated_score is not None and (self.moderated_score < 0 or self.moderated_score > max_score):
            raise ValidationError("Moderated score must be between 0 and the task maximum score.")

    def save(self, *args, **kwargs):
        if self.pk and self.is_locked:
            original = ContinuousAssessmentRecord.objects.get(pk=self.pk)
            changed = (
                original.raw_score != self.raw_score
                or original.moderated_score != self.moderated_score
                or original.teacher_comment != self.teacher_comment
                or original.evidence_reference != self.evidence_reference
                or original.moderation_status != self.moderation_status
            )
            if changed:
                raise ValidationError("This CA record is locked and cannot be edited.")
        super().save(*args, **kwargs)

    @property
    def effective_score(self):
        return self.moderated_score if self.moderated_score is not None else self.raw_score


class CAModeration(models.Model):
    class Status(models.TextChoices):
        PENDING = "PENDING", "Pending"
        APPROVED = "APPROVED", "Approved"
        REJECTED = "REJECTED", "Rejected"
        ADJUSTED = "ADJUSTED", "Adjusted"

    record = models.OneToOneField(
        ContinuousAssessmentRecord,
        on_delete=models.CASCADE,
        related_name="moderation",
    )
    status = models.CharField(max_length=20, choices=Status.choices, default=Status.PENDING)
    moderated_score = models.DecimalField(max_digits=6, decimal_places=2, null=True, blank=True)
    comments = models.TextField(blank=True, null=True)
    moderated_by = models.ForeignKey(
        User,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="ca_moderations_done",
    )
    moderated_at = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ["-moderated_at"]
        db_table = "app_camoderation"

    def __str__(self):
        return f"{self.record} - {self.status}"
