from django.contrib.auth import get_user
from django.contrib.auth.base_user import BaseUserManager, AbstractBaseUser
from django.contrib.auth.hashers import make_password
from django.contrib.auth.models import PermissionsMixin, Group, Permission, User
from django.db import models, transaction
from django.utils.translation import gettext_lazy as _
from qr_manager.models import Batch
from django.utils import timezone
from datetime import timedelta
from django.core.exceptions import ValidationError


class Company(models.Model):
    name = models.CharField(max_length=100)
    number_of_logins_allowed = models.IntegerField(default=0)
    redirect_url = models.CharField(max_length=100, default="")
    logo_on_qr = models.ImageField(upload_to='media/static/company_logos/', blank=True, null=True)
    # finance
    cost_per_qr_code = models.IntegerField(default=20)
    total_amount = models.IntegerField(default=0)

    created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)

    is_active = models.BooleanField(default=True)
    is_deleted = models.BooleanField(default=False)

    def __str__(self):
        return self.name


class CustomAccountManager(BaseUserManager):
    def create_superuser(self, phone, password, **other_fields):
        other_fields.setdefault('is_staff', True)
        other_fields.setdefault('is_superuser', True)
        other_fields.setdefault('is_active', True)
        if other_fields.get('is_staff') is not True:
            raise ValueError('Superuser must be assigned to is_staff=True')
        if other_fields.get('is_superuser') is not True:
            raise ValueError('Superuser must be assigned to is_superuser=True')
        return self.create_user(phone, password, **other_fields)

    def create_user(self, phone, password, **other_fields):
        if not phone:
            raise ValueError(_('You must provide an phone number'))
        user = self.model(phone=phone, **other_fields)
        user.set_password(password)
        user.save()
        return user


class CompanyUser(AbstractBaseUser, PermissionsMixin):
    email = models.EmailField(unique=True)
    name = models.CharField(max_length=255)
    phone = models.CharField(max_length=15, unique=True)
    company = models.ForeignKey(Company, on_delete=models.SET_NULL, null=True, blank=True)
    role = models.CharField(max_length=100, default="Admin")
    address = models.TextField(blank=True, null=True)

    # batch = models.ForeignKey(Batch, on_delete=models.SET_NULL, null=True, blank=True)
    # username = models.CharField(max_length=255, unique=True)
    password = models.CharField(max_length=128)

    # user status
    is_active = models.BooleanField(default=False)
    is_staff = models.BooleanField(default=False)
    is_deleted = models.BooleanField(default=False)
    is_superuser = models.BooleanField(default=False)

    groups = models.ManyToManyField(Group, related_name='company_groups', blank=True)
    user_permissions = models.ManyToManyField(Permission, related_name='company_user_permissions', blank=True)

    created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)

    objects = CustomAccountManager()

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = ['phone']

    class Meta:
        verbose_name_plural = "Company Users"

    def __str__(self):
        return self.email

    def save(self, *args, **kwargs):
        if self.company:
            self.company_name = self.company.name
        else:
            self.company_name = ''
        if self.password and not self.password.startswith(('pbkdf2_sha256$', 'bcrypt', 'argon2')):
            self.password = make_password(self.password)
        super().save(*args, **kwargs)


GENDER_CHOICES = [
    ('Male', 'Male'),
    ('Female', 'Female'),
    ('Other', 'Other'),
]


class Customers(AbstractBaseUser, PermissionsMixin):
    qr_code = models.CharField(max_length=100, unique=True)

    first_name = models.CharField(max_length=255, blank=True)
    last_name = models.CharField(max_length=255, blank=True)
    gender = models.CharField(max_length=10, choices=GENDER_CHOICES, default="Male")
    dob = models.DateField(blank=True,null=True)
    height_feet = models.CharField(max_length=4, blank=True)
    height_inches = models.CharField(max_length=4, blank=True)
    weight = models.CharField(max_length=4, blank=True)

    address_line1 = models.CharField(max_length=255, blank=True, null=True)
    address_line2 = models.CharField(max_length=255, blank=True, null=True)
    city = models.CharField(max_length=255, blank=True, null=True)
    state = models.CharField(max_length=255, blank=True, null=True)
    pin = models.CharField(max_length=10, blank=True, null=True)

    phone = models.CharField(max_length=13, blank=True)
    email = models.EmailField(blank=True)
    profile_image = models.ImageField(upload_to="media/static/user_profile_image/",
                                      default='media/static/user_profile_image/logo.png')
    password = models.CharField(max_length=128, blank=True)
    valid_upto = models.DateField(blank=True, null=True)
    registration_date = models.DateField(blank=True, null=True)

    # Emergency Details
    emergency_phone1 = models.CharField(max_length=13, blank=True, null=True)
    emergency_email1 = models.EmailField(blank=True, null=True)
    emergency_name1 = models.CharField(max_length=255, blank=True, null=True)

    emergency_phone2 = models.CharField(max_length=13, blank=True, null=True)
    emergency_email2 = models.EmailField(blank=True, null=True)
    emergency_name2 = models.CharField(max_length=255, blank=True, null=True)

    primary_physician_name = models.CharField(max_length=254, blank=True)
    primary_physician_contact = models.CharField(max_length=13, blank=True)
    blood_group = models.CharField(max_length=50, blank=True)
    medical_history = models.TextField(blank=True)
    allergies = models.TextField(blank=True)

    # user status
    is_active = models.BooleanField(default=False, verbose_name='status')
    is_deleted = models.BooleanField(default=False)

    created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)
    batch = models.ForeignKey(Batch, on_delete=models.SET_NULL, null=True, blank=True)

    groups = models.ManyToManyField(Group, related_name='customers_groups', blank=True)
    user_permissions = models.ManyToManyField(Permission, related_name='customers_user_permissions', blank=True)

    USERNAME_FIELD = 'qr_code'

    class Meta:
        verbose_name_plural = "Customers"

    def __str__(self):
        return self.qr_code

    """def save(self, *args, **kwargs):
        if self.password and not self.password.startswith(('pbkdf2_sha256$', 'bcrypt', 'argon2')):
            self.password = make_password(self.password)

        self.valid_upto = timezone.now().date() + timedelta(days=2 * 365)
        super().save(*args, **kwargs)"""

    def save(self, *args, **kwargs):
        if self.pk and self.password:
            original_password = Customers.objects.get(pk=self.pk).password
            if not original_password and not self.password.startswith(('pbkdf2_sha256$', 'bcrypt', 'argon2')):
                # First-time password setting
                self.password = make_password(self.password)
                self.valid_upto = timezone.now().date() + timedelta(days=2 * 365)
                self.registration_date = timezone.now().date()
        elif not self.password and self.pk:  # New user with password
            self.password = make_password(self.password)
            self.valid_upto = timezone.now().date() + timedelta(days=2 * 365)
            self.registration_date = timezone.now().date()

        super().save(*args, **kwargs)

    @classmethod
    def deactivate_expired_users(cls):
        # accounts/management/commands/deactivate_expired_users.py
        # this function can be triggered using management command
        # need to add this command in cron jobs in productions
        # example for cronjob is as below for daily midnight
        # 0 0 * * * /var/www/emergency_qr/venv/bin/python /var/www/emergency_qr/manage.py deactivate_expired_users
        today = timezone.now().date()
        expired_users = cls.objects.filter(valid_upto__lt=today, is_active=True)
        for user in expired_users:
            user.is_active = False
            user.save()

    @classmethod
    def reactivate_user(cls, user_id, company_user_id, years=1):
        # this method can be called to reactivate users for upcoming users as they can pay for the same
        user = cls.objects.get(pk=user_id)
        company_user = CompanyUser.objects.get(pk=company_user_id)

        user.is_active = True
        user.valid_upto = timezone.now().date() + timedelta(days=years * 365)
        user.save()

        ReactivationLog.objects.create(
            company_user=company_user,
            customer=user,
            years_extended=years
        )


def validate_file_size(value):
    filesize = value.size
    if filesize > 10485760:  # 10 MB limit
        raise ValidationError("The maximum file size that can be uploaded is 10MB")
    return value


class CustomerDocument(models.Model):
    customer = models.ForeignKey(Customers, related_name='documents', on_delete=models.CASCADE)
    doc_name = models.CharField(max_length=255, blank=True)
    document = models.FileField(upload_to='media/customer_documents/', validators=[validate_file_size])
    uploaded_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return f"{self.customer.qr_code} - {self.document.name}"

    class Meta:
        verbose_name_plural = "Customer Documents"


class ReactivationLog(models.Model):
    company_user = models.ForeignKey(CompanyUser, on_delete=models.CASCADE)
    customer = models.ForeignKey(Customers, on_delete=models.CASCADE)
    reactivated_on = models.DateTimeField(auto_now_add=True)
    years_extended = models.IntegerField()

    created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)

    def __str__(self):
        return f"{self.company_user.email} reactivated {self.customer.qr_code} for {self.years_extended} years"


class Transactions(models.Model):
    company = models.ForeignKey(Company, on_delete=models.CASCADE)
    amount = models.IntegerField()
    transaction_updated_by = models.ForeignKey(CompanyUser, on_delete=models.CASCADE,related_name="transaction_updated_by")

    note = models.TextField(blank=True, null=True)

    created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)

    def save(self, *args, **kwargs):

        with transaction.atomic():
            # If this is a new transaction, adjust the company's total_amount
            if self._state.adding:
                self.company.total_amount -= self.amount
                self.company.save()
            super(Transactions, self).save(*args, **kwargs)

