from django.db import models
from django.core.validators import MinValueValidator, MaxValueValidator
from django.utils import timezone
from .utils.upload_paths import leave_document_upload_path

# =====================================================
# ATTENDANCE SESSION
# One session = one roll call (class + date + period)
# =====================================================


class AttendanceSession(models.Model):

    SESSION_TYPE_CHOICES = [
        ("MORNING", "Morning"),
        ("AFTERNOON", "Afternoon"),
        ("PERIOD", "Period"),
        ("FULL_DAY", "Full Day"),
    ]

    STATUS_CHOICES = [
        ("OPEN", "Open"),  # teacher is actively marking
        ("SUBMITTED", "Submitted"),  # submitted, can be edited within window
        ("LOCKED", "Locked"),  # locked by admin, no further edits
    ]

    academic_class = models.ForeignKey(
        "academics.AcademicClass",
        on_delete=models.CASCADE,
        related_name="attendance_sessions",
    )
    subject = models.ForeignKey(
        "academics.Subject",
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name="attendance_sessions",
        help_text="Populated only for period-level attendance",
    )
    taken_by = models.ForeignKey(
        "people.Teacher",
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name="attendance_sessions_taken",
    )

    date = models.DateField()
    period_number = models.PositiveSmallIntegerField(
        null=True,
        blank=True,
        help_text="Timetable period number; null for full-day sessions",
    )
    session_type = models.CharField(
        max_length=20,
        choices=SESSION_TYPE_CHOICES,
        default="FULL_DAY",
    )
    start_time = models.TimeField(null=True, blank=True)
    end_time = models.TimeField(null=True, blank=True)

    status = models.CharField(
        max_length=20,
        choices=STATUS_CHOICES,
        default="OPEN",
    )

    # Quick stats — updated when attendance is saved
    total_students = models.PositiveIntegerField(default=0)
    present_count = models.PositiveIntegerField(default=0)
    absent_count = models.PositiveIntegerField(default=0)
    late_count = models.PositiveIntegerField(default=0)
    leave_count = models.PositiveIntegerField(default=0)

    remarks = models.TextField(blank=True)
    is_active = models.BooleanField(default=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        # Prevent duplicate sessions for the same class/date/period
        unique_together = ("academic_class", "date", "period_number", "session_type")
        ordering = ["-date", "period_number"]
        indexes = [
            models.Index(fields=["date", "academic_class"]),
            models.Index(fields=["taken_by", "date"]),
            models.Index(fields=["status"]),
        ]

    def __str__(self):
        return f"{self.academic_class} | {self.date} | {self.session_type}"

    def update_counts(self):
        """Recompute denormalised counts from child records."""
        qs = self.student_attendances.filter(is_active=True)
        self.total_students = qs.count()
        self.present_count = qs.filter(status="PRESENT").count()
        self.absent_count = qs.filter(status="ABSENT").count()
        self.late_count = qs.filter(status="LATE").count()
        self.leave_count = qs.filter(status="LEAVE").count()
        self.save(
            update_fields=[
                "total_students",
                "present_count",
                "absent_count",
                "late_count",
                "leave_count",
            ]
        )


# =====================================================
# STUDENT ATTENDANCE
# One row per student per session
# =====================================================


class StudentAttendance(models.Model):

    STATUS_CHOICES = [
        ("PRESENT", "Present"),
        ("ABSENT", "Absent"),
        ("LATE", "Late"),
        ("LEAVE", "On Approved Leave"),
        ("HALF_DAY", "Half Day"),
        ("HOLIDAY", "Holiday / School Closed"),
    ]

    session = models.ForeignKey(
        AttendanceSession,
        on_delete=models.CASCADE,
        related_name="student_attendances",
    )
    enrollment = models.ForeignKey(
        "academics.StudentEnrollment",
        on_delete=models.CASCADE,
        related_name="attendances",
        help_text="Ties attendance to the student's class and academic year",
    )
    # Denormalised for fast querying without always joining through enrollment
    student = models.ForeignKey(
        "people.Student",
        on_delete=models.CASCADE,
        related_name="attendances",
    )

    status = models.CharField(max_length=20, choices=STATUS_CHOICES)
    remarks = models.CharField(max_length=255, blank=True)

    # For LATE status — how many minutes late
    late_minutes = models.PositiveSmallIntegerField(
        null=True,
        blank=True,
        help_text="Minutes late; only relevant when status=LATE",
    )

    # Link to leave record if status=LEAVE
    leave = models.ForeignKey(
        "AttendanceLeave",
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name="attendance_records",
    )

    # Who marked / last edited
    marked_by = models.ForeignKey(
        "people.Teacher",
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name="attendances_marked",
    )
    marked_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    is_active = models.BooleanField(default=True)

    class Meta:
        unique_together = ("session", "enrollment")
        ordering = ["session__date", "enrollment__roll_number"]
        indexes = [
            models.Index(fields=["student", "session"]),
            models.Index(fields=["enrollment", "status"]),
            models.Index(fields=["status"]),
        ]

    def __str__(self):
        return f"{self.student} | {self.session.date} | {self.status}"

    def save(self, *args, **kwargs):
        super().save(*args, **kwargs)
        # Keep session counts in sync automatically
        self.session.update_counts()


# =====================================================
# ATTENDANCE LEAVE
# Approved leave requests — referenced by StudentAttendance
# =====================================================


class AttendanceLeave(models.Model):

    LEAVE_TYPE_CHOICES = [
        ("MEDICAL", "Medical"),
        ("FAMILY", "Family"),
        ("EMERGENCY", "Emergency"),
        ("SPORTS", "Sports / Event"),
        ("OTHER", "Other"),
    ]

    STATUS_CHOICES = [
        ("PENDING", "Pending"),
        ("APPROVED", "Approved"),
        ("REJECTED", "Rejected"),
    ]

    enrollment = models.ForeignKey(
        "academics.StudentEnrollment",
        on_delete=models.CASCADE,
        related_name="leaves",
    )
    student = models.ForeignKey(
        "people.Student",
        on_delete=models.CASCADE,
        related_name="leaves",
    )

    leave_type = models.CharField(max_length=20, choices=LEAVE_TYPE_CHOICES)
    from_date = models.DateField()
    to_date = models.DateField()
    reason = models.TextField()
    document = models.FileField(
        upload_to=leave_document_upload_path,
        null=True,
        blank=True,
        help_text="Medical certificate or supporting document",
    )

    status = models.CharField(
        max_length=20,
        choices=STATUS_CHOICES,
        default="PENDING",
    )
    approved_by = models.ForeignKey(
        "people.Teacher",
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name="leaves_approved",
    )
    approved_at = models.DateTimeField(null=True, blank=True)
    rejection_reason = models.TextField(blank=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 = ["-from_date"]
        indexes = [
            models.Index(fields=["student", "from_date"]),
            models.Index(fields=["status"]),
            models.Index(fields=["enrollment", "from_date", "to_date"]),
        ]

    def __str__(self):
        return (
            f"{self.student} | {self.from_date} to {self.to_date} | {self.leave_type}"
        )

    @property
    def total_days(self):
        return (self.to_date - self.from_date).days + 1


# =====================================================
# ATTENDANCE SUMMARY
# Denormalised monthly rollup — recomputed on save
# Avoids expensive COUNT queries on reports
# =====================================================


class AttendanceSummary(models.Model):

    enrollment = models.ForeignKey(
        "academics.StudentEnrollment",
        on_delete=models.CASCADE,
        related_name="attendance_summaries",
    )
    student = models.ForeignKey(
        "people.Student",
        on_delete=models.CASCADE,
        related_name="attendance_summaries",
    )
    academic_class = models.ForeignKey(
        "academics.AcademicClass",
        on_delete=models.CASCADE,
        related_name="attendance_summaries",
    )

    month = models.PositiveSmallIntegerField(
        validators=[MinValueValidator(1), MaxValueValidator(12)],
    )
    year = models.PositiveSmallIntegerField()

    # Working day counts for the month
    total_working_days = models.PositiveSmallIntegerField(default=0)
    present_days = models.PositiveSmallIntegerField(default=0)
    absent_days = models.PositiveSmallIntegerField(default=0)
    late_days = models.PositiveSmallIntegerField(default=0)
    leave_days = models.PositiveSmallIntegerField(default=0)
    half_days = models.PositiveSmallIntegerField(default=0)

    attendance_percentage = models.DecimalField(
        max_digits=5,
        decimal_places=2,
        default=0.00,
    )

    # Flag when student falls below school's minimum threshold
    is_below_minimum = models.BooleanField(default=False)

    last_computed_at = models.DateTimeField(auto_now=True)

    class Meta:
        unique_together = ("enrollment", "month", "year")
        ordering = ["-year", "-month"]
        indexes = [
            models.Index(fields=["student", "year", "month"]),
            models.Index(fields=["academic_class", "year", "month"]),
            models.Index(fields=["is_below_minimum"]),
        ]

    def __str__(self):
        return (
            f"{self.student} | {self.month}/{self.year} | {self.attendance_percentage}%"
        )

    def compute(self):
        """Recompute this summary from raw StudentAttendance records."""
        from django.db.models import Count, Q

        records = StudentAttendance.objects.filter(
            enrollment=self.enrollment,
            session__date__year=self.year,
            session__date__month=self.month,
            is_active=True,
        )

        self.total_working_days = records.count()
        self.present_days = records.filter(status="PRESENT").count()
        self.absent_days = records.filter(status="ABSENT").count()
        self.late_days = records.filter(status="LATE").count()
        self.leave_days = records.filter(status="LEAVE").count()
        self.half_days = records.filter(status="HALF_DAY").count()

        # Late counts as present for percentage; half-day counts as 0.5
        effective_present = self.present_days + self.late_days + (self.half_days * 0.5)
        if self.total_working_days > 0:
            self.attendance_percentage = round(
                (effective_present / self.total_working_days) * 100, 2
            )
        else:
            self.attendance_percentage = 0.00

        # Check against class minimum (default 75%)
        min_pct = (
            self.academic_class.min_attendance_percentage
            if self.academic_class.min_attendance_percentage
            else 75.00
        )
        self.is_below_minimum = self.attendance_percentage < min_pct
        self.save()
