from django.db import models
import datetime
from django.contrib.auth.models import User
from django.utils import timezone
from PIL import Image
from io import BytesIO
from django.core.files.base import ContentFile
from django.conf import settings

# Create your models here.

class School(models.Model):
    name = models.CharField(max_length=255)
    code = models.CharField(max_length=50, unique=True)
    admin_username = models.CharField(max_length=100, null=True, blank=True)
    password = models.CharField(max_length=255, null=True, blank=True)
    address = models.TextField(null=True, blank=True)
    is_sms_enabled = models.BooleanField(default=False)
    is_hold_enabled = models.BooleanField(default=True)
    is_time_test_enabled = models.BooleanField(default=False)
    is_time_test_active = models.BooleanField(default=False)
    test_timestamp = models.DateTimeField(null=True, blank=True)
    test_set_at = models.DateTimeField(null=True, blank=True)
    
    # Announcement SMS Quota
    monthly_announcement_limit = models.IntegerField(default=5)
    announcements_sent_this_month = models.IntegerField(default=0)
    last_announcement_reset = models.DateField(default=datetime.date.today)
    is_individual_sms_enabled = models.BooleanField(default=False)
    is_lunch_break_required = models.BooleanField(default=False)  # If True, lunch break punch is mandatory
    subscription_end_date = models.DateField(null=True, blank=True)
    

    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.name

class UserProfile(models.Model):
    ROLE_CHOICES = (
        ('SUPERUSER', 'Superuser'),
        ('SCHOOL_ADMIN', 'School Admin'),
        ('TEACHER', 'Teacher'),
    )
    user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile')
    role = models.CharField(max_length=20, choices=ROLE_CHOICES, default='SCHOOL_ADMIN')
    school = models.ForeignKey(School, on_delete=models.SET_NULL, null=True, blank=True, related_name='admins')
    
    # Teacher specific fields
    phone_number = models.CharField(max_length=20, null=True, blank=True)
    nid = models.CharField(max_length=50, null=True, blank=True)
    address = models.TextField(null=True, blank=True)
    dob = models.DateField(null=True, blank=True)
    qualification = models.TextField(null=True, blank=True)

    def __str__(self):
        return f"{self.user.username} - {self.role}"

class StudentClass(models.Model):
    school = models.ForeignKey(School, on_delete=models.CASCADE, related_name='classes')
    name = models.CharField(max_length=100)
    teacher = models.ForeignKey('UserProfile', on_delete=models.SET_NULL, null=True, blank=True, related_name='assigned_classes', limit_choices_to={'role': 'TEACHER'})
    subjects = models.ManyToManyField('Subject', blank=True, related_name='classes')
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return f"{self.name} - {self.school.name}"

def student_photo_upload_path(instance, filename):
    # school code ensures per-institution isolation in media storage
    return f'students/{instance.school.code}/{filename}'

class Student(models.Model):
    school = models.ForeignKey(School, on_delete=models.CASCADE, related_name='students')
    student_class = models.ForeignKey(StudentClass, on_delete=models.SET_NULL, null=True, blank=True, related_name='students')
    pin = models.CharField(max_length=50) # Student ID for device
    roll = models.IntegerField(null=True, blank=True) # Academic Roll
    name = models.CharField(max_length=255)
    photo = models.ImageField(upload_to=student_photo_upload_path, null=True, blank=True)
    father_name = models.CharField(max_length=255, null=True, blank=True)
    mother_name = models.CharField(max_length=255, null=True, blank=True)
    dob = models.DateField(null=True, blank=True)
    birth_certificate_number = models.CharField(max_length=100, null=True, blank=True)
    contact_number = models.CharField(max_length=20, null=True, blank=True)
    address = models.TextField(null=True, blank=True)
    admission_date = models.DateField(default=timezone.now)
    subjects = models.ManyToManyField('Subject', blank=True, related_name='students')
    late_cleared_until = models.DateField(null=True, blank=True)
    absent_cleared_until = models.DateField(null=True, blank=True)
    is_active = models.BooleanField(default=True)
    is_sms_enabled = models.BooleanField(default=True) # Per-student SMS control
    secret_code = models.CharField(max_length=10, null=True, blank=True)  # Auto-generated admission code
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        unique_together = ('school', 'pin')

    def __str__(self):
        return f"{self.name} ({self.pin}) - {self.school.name}"

    def save(self, *args, **kwargs):
        if self.photo:
            try:
                # Compression Logic
                img = Image.open(self.photo)
                
                # Check if it's already an image we've processed (optional but safer)
                # For simplicity, we process whenever self.photo is an instance of UploadedFile
                # or just process every time if it's small enough to not be an issue.
                
                if img.mode != 'RGB':
                    img = img.convert('RGB')
                
                # Step 1: Resize to a standard profile size
                # 300px is usually plenty for profile thumbnails
                max_size = (300, 300)
                if img.width > 300 or img.height > 300:
                    img.thumbnail(max_size, Image.LANCZOS)
                
                # Step 2: Progressively compress
                quality = 70
                output = BytesIO()
                img.save(output, format='JPEG', quality=quality)
                
                # Target: 10-15 KB (15360 bytes)
                # If larger than 15 KB, reduce quality
                while output.tell() > 15360 and quality > 20:
                    quality -= 10
                    output = BytesIO()
                    img.save(output, format='JPEG', quality=quality)
                
                # If still too large, force a smaller resize
                if output.tell() > 15360:
                    img.thumbnail((200, 200), Image.LANCZOS)
                    output = BytesIO()
                    img.save(output, format='JPEG', quality=30)

                # Update the photo field with the compressed version
                # Only if we actually did some work or if it's a new upload
                file_name = self.photo.name
                if not file_name.lower().endswith('.jpg'):
                    file_name = file_name.rsplit('.', 1)[0] + '.jpg'
                
                self.photo = ContentFile(output.getvalue(), name=file_name)
            except Exception as e:
                # If something fails (e.g. not an image), just let it pass or log it
                pass

        super().save(*args, **kwargs)
        # Create a promotion record if it doesn't exist for the current class
        if self.student_class:
            last_promo = PromotionRecord.objects.filter(student=self).order_by('-promoted_at').first()
            if not last_promo or last_promo.student_class != self.student_class:
                PromotionRecord.objects.create(
                    student=self,
                    student_class=self.student_class,
                    roll=self.roll
                )

class PromotionRecord(models.Model):
    student = models.ForeignKey(Student, on_delete=models.CASCADE, related_name='promotions')
    student_class = models.ForeignKey(StudentClass, on_delete=models.CASCADE)
    roll = models.IntegerField(null=True, blank=True)
    promoted_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ['-promoted_at']

    def __str__(self):
        return f"{self.student.name} - {self.student_class.name}"

class FeeType(models.Model):
    school = models.ForeignKey(School, on_delete=models.CASCADE, related_name='fee_types')
    name = models.CharField(max_length=255)
    default_amount = models.DecimalField(max_digits=10, decimal_places=2, default=0.00)
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return f"{self.name} - {self.school.name}"

class DetectionFee(models.Model):
    school = models.ForeignKey(School, on_delete=models.CASCADE, related_name='detection_fees')
    reason = models.CharField(max_length=255) # e.g. Absent, Late
    amount = models.DecimalField(max_digits=10, decimal_places=2, default=0.00)
    grace_minutes = models.IntegerField(default=0) # Only for 'Late' detection
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return f"{self.reason} - {self.school.name}"

class ClassSchedule(models.Model):
    school = models.ForeignKey(School, on_delete=models.CASCADE, related_name='schedules')
    student_class = models.OneToOneField(StudentClass, on_delete=models.CASCADE, related_name='schedule')
    punch_in_start = models.TimeField()
    punch_in_end = models.TimeField()
    checkout_start = models.TimeField()
    checkout_end = models.TimeField()
    lunch_start = models.TimeField(null=True, blank=True)
    lunch_end = models.TimeField(null=True, blank=True)
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return f"{self.student_class.name} Schedule - {self.school.name}"

class StudentLeave(models.Model):
    school = models.ForeignKey(School, on_delete=models.CASCADE, related_name='leaves')
    student = models.ForeignKey(Student, on_delete=models.CASCADE, related_name='leaves')
    start_date = models.DateField()
    end_date = models.DateField()
    leave_type = models.CharField(max_length=100) # e.g. Casual, Medical
    reason = models.TextField(null=True, blank=True)
    is_approved = models.BooleanField(default=True) # Assume approved for simplicity in this MVP
    is_deleted = models.BooleanField(default=False)
    deleted_at = models.DateTimeField(null=True, blank=True)
    created_at = models.DateTimeField(auto_now_add=True)

    @property
    def total_days(self):
        return (self.end_date - self.start_date).days + 1

    def __str__(self):
        return f"{self.student.name} - {self.leave_type} ({self.start_date} to {self.end_date})"

class Holiday(models.Model):
    DAY_TYPES = (
        ('HOLIDAY', 'Holiday'),
        ('WORKING_DAY', 'Working Day'),
    )
    school = models.ForeignKey(School, on_delete=models.CASCADE, related_name='holidays')
    date = models.DateField()
    note = models.CharField(max_length=255)
    day_type = models.CharField(max_length=20, choices=DAY_TYPES, default='HOLIDAY')
    is_full_day = models.BooleanField(default=True)
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return f"{self.note} ({self.date}) - {self.school.name} ({self.day_type})"

class StudentFee(models.Model):
    student = models.ForeignKey(Student, on_delete=models.CASCADE, related_name='fees')
    fee_type = models.ForeignKey(FeeType, on_delete=models.CASCADE)
    amount = models.DecimalField(max_digits=10, decimal_places=2)
    paid_amount = models.DecimalField(max_digits=10, decimal_places=2, default=0.00)
    is_paid = models.BooleanField(default=False)
    student_class = models.ForeignKey(StudentClass, on_delete=models.SET_NULL, null=True, blank=True, related_name='recorded_fees')
    paid_at = models.DateTimeField(null=True, blank=True)
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return f"{self.student.name} - {self.fee_type.name} ({'Paid' if self.is_paid else 'Unpaid'})"

    @property
    def due(self):
        return self.amount - self.paid_amount

class SMSLog(models.Model):
    school = models.ForeignKey(School, on_delete=models.CASCADE, related_name='sms_logs')
    recipient = models.CharField(max_length=20)
    message = models.TextField()
    status = models.CharField(max_length=50, default='SENT')
    timestamp = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return f"{self.school.name} -> {self.recipient} ({self.status})"

class Device(models.Model):
    serial_number = models.CharField(max_length=100, unique=True)
    name = models.CharField(max_length=255, default="ZK Device")
    school = models.ForeignKey(School, on_delete=models.SET_NULL, null=True, blank=True, related_name='devices')
    assigned_class = models.ForeignKey(StudentClass, on_delete=models.SET_NULL, null=True, blank=True, related_name='assigned_devices')
    is_authorized = models.BooleanField(default=False)
    is_terminated = models.BooleanField(default=False)
    last_seen = models.DateTimeField(auto_now=True)
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return f"{self.name} ({self.serial_number}) {'[TERMINATED]' if self.is_terminated else ''}"

class GlobalSMSSettings(models.Model):
    """Platform-wide SMS settings managed by Superuser via BulkSMSBD."""
    api_key = models.CharField(max_length=255, blank=True, null=True)
    sender_id = models.CharField(max_length=100, blank=True, null=True)
    is_active = models.BooleanField(default=False)
    site_domain = models.CharField(
        max_length=255, blank=True, null=True,
        default='',
        help_text="Production domain for SMS links, e.g. https://school.example.com (no trailing slash)"
    )
    
    # Templates — use these variables: {student_name}, {status}, {time}, {history_url}
    msg_check_in = models.TextField(
        default="Dear, Your Child {student_name} {status} at {time}. Details: {history_url}",
        help_text="Variables: {student_name}, {status}, {time}, {history_url}"
    )
    msg_check_out = models.TextField(
        default="Dear, Your Child {student_name} {status} at {time}. Details: {history_url}",
        help_text="Variables: {student_name}, {status}, {time}, {history_url}"
    )


    def __str__(self):
        return "Global SMS Settings"

    @classmethod
    def get_settings(cls):
        obj, _ = cls.objects.get_or_create(pk=1)
        return obj


class AttendanceLog(models.Model):
    student = models.ForeignKey(Student, on_delete=models.CASCADE, related_name='logs', null=True, blank=True)
    school = models.ForeignKey(School, on_delete=models.CASCADE, related_name='attendance_records')
    device = models.ForeignKey(Device, on_delete=models.SET_NULL, null=True, blank=True)
    timestamp = models.DateTimeField()
    check_type = models.CharField(max_length=10, default='IN') # IN, OUT
    status = models.CharField(max_length=50, null=True, blank=True) # EARLY_IN, PRESENT_IN, LATE, EARLY_OUT, PRESENT_OUT, ABSENT
    unidentified_pin = models.CharField(max_length=50, null=True, blank=True)
    is_manually_edited = models.BooleanField(default=False)
    created_at = models.DateTimeField(auto_now_add=True)
    student_class = models.ForeignKey(StudentClass, on_delete=models.SET_NULL, null=True, blank=True, related_name='attendance_logs')

    class Meta:
        ordering = ['-timestamp']

class FeeTransaction(models.Model):
    """Stores a financial transaction history for fee collection."""
    METHODS = (
        ('CASH', 'Cash'), ('BKASH', 'bKash'), ('NOGOD', 'Nagad'),
        ('ROCKET', 'Rocket'), ('BANK', 'Bank'), ('OTHERS', 'Others')
    )
    school = models.ForeignKey(School, on_delete=models.CASCADE, related_name='fee_transactions')
    student = models.ForeignKey(Student, on_delete=models.CASCADE, related_name='fee_history')
    total_amount = models.DecimalField(max_digits=10, decimal_places=2)
    discount_amount = models.DecimalField(max_digits=10, decimal_places=2, default=0.00)
    fine_amount = models.DecimalField(max_digits=10, decimal_places=2, default=0.00)
    payment_method = models.CharField(max_length=20, choices=METHODS, default='CASH')
    reference_number = models.CharField(max_length=100, blank=True, null=True)
    student_class = models.ForeignKey(StudentClass, on_delete=models.SET_NULL, null=True, blank=True, related_name='fee_transactions')
    timestamp = models.DateTimeField(auto_now_add=True)
    
    def __str__(self):
        return f"RECPT-{self.id} | {self.student.name} | {self.total_amount}"

class FeeTransactionItem(models.Model):
    """Breakdown of individual items covered by a single transaction."""
    transaction = models.ForeignKey(FeeTransaction, on_delete=models.CASCADE, related_name='items')
    fee = models.ForeignKey(StudentFee, on_delete=models.CASCADE)
    amount_paid = models.DecimalField(max_digits=10, decimal_places=2)
    
    def __str__(self):
        return f"{self.fee.fee_type.name} - {self.amount_paid}"

class AdmitPass(models.Model):
    """Academic permit/pass issued for specific events or terms."""
    school = models.ForeignKey(School, on_delete=models.CASCADE, related_name='admit_passes')
    student = models.ForeignKey(Student, on_delete=models.CASCADE, related_name='issued_passes')
    title = models.CharField(max_length=255, help_text="e.g. Half Yearly Exam 2026")
    start_date = models.DateField()
    end_date = models.DateField()
    issued_date = models.DateField(auto_now_add=True)
    note = models.TextField(blank=True, null=True)
    
    def __str__(self):
        return f"{self.title} - {self.student.name}"

class GradeConfig(models.Model):
    school = models.ForeignKey(School, on_delete=models.CASCADE, related_name='grade_configs')
    grade_name = models.CharField(max_length=10) # e.g. A+
    min_marks = models.DecimalField(max_digits=5, decimal_places=2)
    max_marks = models.DecimalField(max_digits=5, decimal_places=2)
    gpa_value = models.DecimalField(max_digits=3, decimal_places=2)
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ['-min_marks']

    def __str__(self):
        return f"{self.grade_name} ({self.min_marks}-{self.max_marks}) - {self.school.name}"

class Exam(models.Model):
    school = models.ForeignKey(School, on_delete=models.CASCADE, related_name='exams')
    name = models.CharField(max_length=255) # e.g. First Term, Final Exam
    year = models.IntegerField(default=2026)
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return f"{self.name} ({self.year}) - {self.school.name}"

class VirtualClass(models.Model):
    school = models.ForeignKey(School, on_delete=models.CASCADE, related_name='virtual_classes')
    student_class = models.ForeignKey(StudentClass, on_delete=models.CASCADE, related_name='virtual_classes')
    teacher = models.ForeignKey(UserProfile, on_delete=models.SET_NULL, null=True, related_name='virtual_classes')
    title = models.CharField(max_length=255)
    join_link = models.URLField()
    meeting_code = models.CharField(max_length=100, null=True, blank=True)
    schedule_time = models.DateTimeField()
    subjects = models.ManyToManyField('Subject', blank=True)
    students = models.ManyToManyField('Student', blank=True, related_name='invited_virtual_classes')
    is_sms_sent = models.BooleanField(default=False)
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        verbose_name_plural = "Virtual Classes"
        ordering = ['-schedule_time']

    def __str__(self):
        return f"{self.title} - {self.student_class.name}"

class SubjectGroup(models.Model):
    school = models.ForeignKey(School, on_delete=models.CASCADE, related_name='subject_groups')
    name = models.CharField(max_length=255) # e.g. Science, Commerce, General
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return f"{self.name} - {self.school.name}"

class Subject(models.Model):
    school = models.ForeignKey(School, on_delete=models.CASCADE, related_name='subjects')
    group = models.ForeignKey(SubjectGroup, on_delete=models.SET_NULL, null=True, blank=True, related_name='subjects')
    name = models.CharField(max_length=255)
    code = models.CharField(max_length=50, null=True, blank=True)
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return f"{self.name} - {self.school.name}"

class ResultRecord(models.Model):
    student = models.ForeignKey(Student, on_delete=models.CASCADE, related_name='results')
    exam = models.ForeignKey(Exam, on_delete=models.CASCADE, related_name='records')
    subject = models.ForeignKey(Subject, on_delete=models.CASCADE, related_name='records')
    marks = models.DecimalField(max_digits=5, decimal_places=2)
    grade = models.CharField(max_length=5, blank=True)
    gpa = models.DecimalField(max_digits=3, decimal_places=2, default=0.00)
    student_class = models.ForeignKey(StudentClass, on_delete=models.SET_NULL, null=True, blank=True, related_name='recorded_results')
    is_sms_sent = models.BooleanField(default=False)
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        unique_together = ('student', 'exam', 'subject')

    def save(self, *args, **kwargs):
        if not self.student_class and self.student.student_class:
            self.student_class = self.student.student_class
            
        # Auto-calculate Grade/GPA using database config if available
        m = float(self.marks)
        configs = GradeConfig.objects.filter(school=self.student.school).order_by('-min_marks')
        
        assigned = False
        for cfg in configs:
            if float(cfg.min_marks) <= m <= float(cfg.max_marks):
                self.grade = cfg.grade_name
                self.gpa = cfg.gpa_value
                assigned = True
                break
        
        if not assigned:
            # Fallback to standard logic if no config
            if m >= 80: self.grade, self.gpa = 'A+', 5.00
            elif m >= 70: self.grade, self.gpa = 'A', 4.00
            elif m >= 60: self.grade, self.gpa = 'A-', 3.50
            elif m >= 50: self.grade, self.gpa = 'B', 3.00
            elif m >= 40: self.grade, self.gpa = 'C', 2.00
            elif m >= 33: self.grade, self.gpa = 'D', 1.00
            else: self.grade, self.gpa = 'F', 0.00
            
        super().save(*args, **kwargs)

    def __str__(self):
        return f"{self.student.name} - {self.subject.name} ({self.exam.name})"

class Announcement(models.Model):
    message = models.TextField()
    schools = models.ManyToManyField(School, related_name='announcements', blank=True)
    is_bulk = models.BooleanField(default=False)  # True if intended for all schools
    created_at = models.DateTimeField(auto_now_add=True)
    
    # Track who has seen the announcement to avoid repetitive popups
    seen_by = models.ManyToManyField(School, related_name='seen_announcements', blank=True)

    def __str__(self):
        return f"Announcement {self.id} - {self.created_at.strftime('%Y-%m-%d')}"
