import calendar
from django.db import models
from django.contrib.auth.models import BaseUserManager, AbstractBaseUser, PermissionsMixin
from ckeditor.fields import RichTextField
from PIL import Image as PilImage
from io import BytesIO
from django.core.files import File
from django.db.models import Max
from django.core.exceptions import ValidationError
from PIL import Image  
from io import BytesIO
from django.core.files.base import ContentFile
import random
from django.utils import timezone
from datetime import timedelta
from django.utils.translation import gettext_lazy as _
from datetime import date
from django.utils import timezone
import datetime
import uuid
import os

class CustomUserManager(BaseUserManager):
    def create_user(self, mobile, name, password=None, **extra_fields):
        if not mobile:
            raise ValueError("Mobile number is required")
        user = self.model(mobile=mobile, name=name, **extra_fields)
        if password:
            user.set_password(password)
        else:
            user.set_unusable_password()
        user.save(using=self._db)
        return user

    def create_superuser(self, mobile, name, password=None, **extra_fields):
        extra_fields.setdefault('is_staff', True)
        extra_fields.setdefault('is_superuser', True)

        return self.create_user(mobile, name, password, **extra_fields)

STATUS = [('', '----------')] + [('Patient', 'রোগী'),('Doctor', 'ডাক্তার')]
SEX = [('', '----------')] + [('Male', 'পুরুষ'), ('Female', 'মহিলা')]

class MyUser(AbstractBaseUser, PermissionsMixin):
    id = models.BigAutoField(primary_key=True) 
    mobile = models.CharField(max_length=11, verbose_name='Phone Number')
    name = models.CharField(max_length=255)
    dob = models.DateField(verbose_name='জন্মতারিখ')
    gender = models.CharField(choices=SEX, max_length=100, verbose_name='লিঙ্গ')
    role = models.CharField(choices=STATUS, max_length=100, verbose_name='আপনি কী?')
    address = models.TextField(blank=True, null=True, verbose_name='ঠিকানা')
    profile_pic = models.ImageField(
        upload_to='hospital_profile',
        verbose_name="আপনার ছবি আপলোড করুন",
        default='hospital_profile/default.jpg'
    )
    created_at = models.DateTimeField(auto_now_add=True)
    def save(self, *args, **kwargs):

        super().save(*args, **kwargs)

        if self.profile_pic and self.profile_pic.name != 'hospital_profile/default.jpg':

            img = Image.open(self.profile_pic.path)

            img = img.convert('RGB')

            img = img.resize((200, 200), Image.LANCZOS)

            img_io = BytesIO()

            img.save(img_io, format='JPEG', quality=85)

            filename = os.path.basename(self.profile_pic.name)
            filename = os.path.splitext(filename)[0] + ".jpg"

            self.profile_pic.save(
                filename,
                ContentFile(img_io.getvalue()),
                save=False
            )

            super().save(update_fields=['profile_pic'])

    is_active = models.BooleanField(default=True)
    is_staff = models.BooleanField(default=False)

    objects = CustomUserManager()

    USERNAME_FIELD = 'id'  # Keep email login for Django admin
    REQUIRED_FIELDS = ['name', 'mobile', 'dob', 'gender', 'role']  # Require phone during registration

    class Meta:
        unique_together = ("mobile", "dob")

    @property
    def age(self):
        today = timezone.localdate()
        dob = self.dob

        years = today.year - dob.year
        months = today.month - dob.month
        days = today.day - dob.day

        if days < 0:
            # get days in previous month
            if today.month == 1:
                prev_month = 12
                prev_year = today.year - 1
            else:
                prev_month = today.month - 1
                prev_year = today.year

            days_in_prev_month = calendar.monthrange(prev_year, prev_month)[1]
            days += days_in_prev_month
            months -= 1

        if months < 0:
            months += 12
            years -= 1

        return {
            "years": years,
            "months": months,
            "days": days
        }

    def __str__(self):
        return f"{self.name}-{self.mobile}"


class OTP(models.Model):
    mobile = models.CharField(max_length=11)
    code = models.CharField(max_length=6)
    created_at = models.DateTimeField(auto_now_add=True)

    def is_valid(self):
        return timezone.now() <= self.created_at + timedelta(minutes=5)
    
    @staticmethod
    def generate_otp():
        return str(random.randint(100000, 999999))

doc_status = [
    ('Yes', 'Yes'),
    ('No', 'No')
]

class Doctor(models.Model):
    vendor = models.OneToOneField(MyUser, on_delete=models.SET_NULL, null=True, related_name="vendor_doctor")
    designation = models.CharField(max_length=100, verbose_name="Designation", null=True, blank=True)
    specialization = models.TextField(verbose_name="Specialization", null=True, blank=True)
    status = models.CharField(choices=doc_status, default='No', max_length=10)
    eye_specialist = models.BooleanField(default=False)
    def __str__(self):
        return f"{self.vendor.name} - {self.specialization}"

    class Meta:
        verbose_name = "Doctor"
        verbose_name_plural = "Doctors"

class Availability(models.Model):
    doctor = models.ForeignKey(Doctor, on_delete=models.SET_NULL, null=True, related_name="availabilities")
    date = models.DateField()
    end_serial_time = models.TimeField(blank=True, null=True)
    start_time = models.TimeField(verbose_name="Start Time")
    end_time = models.TimeField(verbose_name="End Time")
    max_serials = models.IntegerField(default=10)

    def __str__(self):
        return f"{self.doctor.vendor.name} - {self.date} ({self.start_time} - {self.end_time})"

    class Meta:
        verbose_name = "Availability"
        verbose_name_plural = "Availabilities"
        ordering = ['doctor', 'date', 'start_time']
    

class Appointment(models.Model):
    doctor = models.ForeignKey(Doctor, on_delete=models.SET_NULL, null=True, related_name="doctor_appointments")
    patient = models.ForeignKey(MyUser, on_delete=models.SET_NULL, null=True, related_name="patient_appointments")
    available = models.ForeignKey(Availability, on_delete=models.SET_NULL, null=True, related_name='appointments')
    notes = models.TextField(verbose_name="Doctor Notes", blank=True, null=True)
    serial = models.PositiveIntegerField(verbose_name="Serial Number", null=True, blank=True)
    created_at = models.DateTimeField(auto_now_add=True)
    def save(self, *args, **kwargs):
        # Get local current time
        now = timezone.localtime()

        # Combine date and end_serial_time for cutoff datetime
        if self.available.end_serial_time:
            cutoff_datetime = datetime.datetime.combine(
                self.available.date,
                self.available.end_serial_time
            )

            # Make cutoff aware of timezone
            cutoff_datetime = timezone.make_aware(cutoff_datetime, timezone.get_current_timezone())

            if now > cutoff_datetime:
                raise ValidationError("সিরিয়াল নেওয়ার সময় শেষ হয়েছে।")

        # Prevent duplicate appointment for same patient/doctor/date
        if not self.pk:  
            if Appointment.objects.filter(
                patient=self.patient,
                available__date=self.available.date,
                doctor__vendor=self.doctor.vendor
            ).exists():
                raise ValidationError("আপনার ইতিমধ্যেই এই দিনের জন্য এবং এই ডাক্তারের সাথে অ্যাপয়েন্টমেন্ট আছে।")

        # Check max serial limit
        total_appointments = Appointment.objects.filter(
            available=self.available
        ).count()
        if total_appointments >= self.available.max_serials:
            raise ValidationError("Serial limit for this availability slot has been reached.")

        # Auto-assign serial
        if not self.serial:
            last_serial = Appointment.objects.filter(
                available=self.available
            ).aggregate(Max('serial'))['serial__max'] or 0
            self.serial = last_serial + 1

        super().save(*args, **kwargs)

    def __str__(self):
        return f"Appointment: {self.patient.name} with {self.doctor.vendor.name}, Serial: {self.serial}"
    

class Prescription(models.Model):
    appointment = models.ForeignKey("Appointment", on_delete=models.SET_NULL, null=True, related_name="prescriptions")
    prescribed_date = models.DateTimeField(auto_now_add=True, verbose_name="প্রেসক্রিপশনের তারিখ")
    symptoms = models.TextField(verbose_name="উপসর্গ", blank=True, null=True)
    on_examination = models.TextField(verbose_name="On Examination", blank=True, null=True)
    instructions = models.TextField(verbose_name="পরামর্শ", blank=True, null=True)
    weight = models.CharField(max_length=50, blank=True, null=True, verbose_name="ওজন")
    temperature = models.CharField(max_length=50, blank=True, null=True, verbose_name="শরীরের তাপমাত্রা")
    blood_pressure = models.CharField(max_length=50, blank=True, null=True, verbose_name="রক্তচাপ")

    # Right Eye
    right_sph_distance = models.CharField(max_length=10, blank=True, null=True)
    right_cyl_distance = models.CharField(max_length=10, blank=True, null=True)
    right_axis_distance = models.CharField(max_length=10, blank=True, null=True)
    right_corrected_distance = models.CharField(max_length=10, blank=True, null=True)

    right_sph_near = models.CharField(max_length=10, blank=True, null=True)
    right_cyl_near = models.CharField(max_length=10, blank=True, null=True)
    right_axis_near = models.CharField(max_length=10, blank=True, null=True)
    right_corrected_near = models.CharField(max_length=10, blank=True, null=True)

    # Left Eye
    left_sph_distance = models.CharField(max_length=10, blank=True, null=True)
    left_cyl_distance = models.CharField(max_length=10, blank=True, null=True)
    left_axis_distance = models.CharField(max_length=10, blank=True, null=True)
    left_corrected_distance = models.CharField(max_length=10, blank=True, null=True)

    left_sph_near = models.CharField(max_length=10, blank=True, null=True)
    left_cyl_near = models.CharField(max_length=10, blank=True, null=True)
    left_axis_near = models.CharField(max_length=10, blank=True, null=True)
    left_corrected_near = models.CharField(max_length=10, blank=True, null=True)

    # Instructions (checkboxes)
    unifocal = models.BooleanField(default=False)
    bifocal = models.BooleanField(default=False)
    cr_39 = models.BooleanField(default=False)
    h_index = models.BooleanField(default=False)
    odd_rt_lt = models.BooleanField(default=False)
    kb = models.BooleanField(default=False)
    valius = models.BooleanField(default=False)
    dee = models.BooleanField(default=False)
    execution = models.BooleanField(default=False)
    arc = models.BooleanField(default=False)
    bluecut = models.BooleanField(default=False, null=True)
    def __str__(self):
        return f"{self.appointment.patient.name} - প্রেসক্রিপশন"

    class Meta:
        verbose_name = "প্রেসক্রিপশন"
        verbose_name_plural = "প্রেসক্রিপশনসমূহ"

class PatientMedicine(models.Model):
    prescription = models.ForeignKey(Prescription, on_delete=models.SET_NULL, null=True, related_name="medicines", verbose_name="প্রেসক্রিপশন")
    name = models.CharField(max_length=255, blank=True, null=True)
    generic = models.CharField(max_length=255, blank=True, null=True)
    strength = models.CharField(max_length=100, blank=True, null=True)
    manufacturer = models.CharField(max_length=255, blank=True, null=True)
    dosage_form = models.CharField(max_length=255, blank=True, null=True)
    
    TIME_CHOICES = [
        ('১+১+১', '১+১+১'),
        ('১+0+১', '১+0+১'),
        ('১+১+0', '১+১+0'),
        ('0+১+১', '0+১+১'),
        ('0+0+১', '0+0+১'),
        ('0+১+0', '0+১+0'),
        ('১+0+0', '১+0+0'),
    ]
    time = models.CharField(max_length=10, choices=TIME_CHOICES, verbose_name="সেবনের সময়সূচী")

    MEAL_CHOICES = [
        ('খাওয়ার পর', 'খাওয়ার পর'),
        ('খাওয়ার আগে', 'খাওয়ার আগে'),
        ('যেকোনো সময়', 'যেকোনো সময়'),
    ]
    meal_time = models.CharField(max_length=20, choices=MEAL_CHOICES, verbose_name="খাওয়ার সময়")

    duration = models.CharField(max_length=50, verbose_name="ব্যবহারের সময়কাল (দিন/সপ্তাহ)")

    def __str__(self):
        return f"{self.name} - {self.time}"

    class Meta:
        verbose_name = "ঔষধ"
        verbose_name_plural = "ঔষধসমূহ"




class Test(models.Model):
    STATUS_CHOICES = [
        ('Pending', 'Pending'),    
        ('Uploaded', 'Uploaded'),  
        ('Reviewed', 'Reviewed')   
    ]
    appointment = models.ForeignKey(Appointment, on_delete=models.SET_NULL, null=True, related_name="tests")
    test_name = models.CharField(max_length=255, verbose_name="Test Name")
    instructions = models.CharField(max_length=255, blank=True, null=True)
    prescribed_date = models.DateTimeField(auto_now_add=True, verbose_name="Prescribed Date")
    status = models.CharField(max_length=10, choices=STATUS_CHOICES, default='Pending', verbose_name="Status")
    def __str__(self):
        return f"{self.test_name} for {self.appointment.patient.name}"

    class Meta:
        verbose_name = "Test"
        verbose_name_plural = "Tests"


class TestReport(models.Model):
    test = models.OneToOneField(Test, on_delete=models.SET_NULL, null=True, related_name="report")
    uploaded_by = models.ForeignKey(MyUser, on_delete=models.SET_NULL, null=True, verbose_name="Uploaded By")
    report_file = models.FileField(upload_to="test_reports/", verbose_name="Report File")
    upload_date = models.DateTimeField(auto_now_add=True, verbose_name="Upload Date")
    notes = models.TextField(blank=True, null=True, verbose_name="Notes")

    def __str__(self):
        return f"Report for {self.test.test_name} uploaded by {self.uploaded_by.name}"

    class Meta:
        verbose_name = "Test Report"
        verbose_name_plural = "Test Reports"


class HActiviti(models.Model):
    a_name = models.CharField(max_length=255)
    a_details = RichTextField()
    created_at = models.DateField(null=True)
    image = models.ImageField(upload_to='hospital_activity')
            
    def __str__(self):
        return self.a_name
    
    def save(self, *args, **kwargs):
        img = PilImage.open(self.image)
        output_size = (500, 375)
        img = img.resize(output_size)
        buffer = BytesIO()
        img_format = 'JPEG' if self.image.name.lower().endswith('.jpg') or self.image.name.lower().endswith('.jpeg') else 'PNG'
        img.save(buffer, format=img_format, quality=70)
        self.image.save(self.image.name, File(buffer), save=False)
        super().save(*args, **kwargs)


class SiteSettings(models.Model):
    maintenance_mode = models.BooleanField(default=False)

    def __str__(self):
        return "Site Settings"