from django.db import models
from django.db.models.functions import Lower


class CRSIdField(models.CharField):
    """
    The CRSIdField class is a character field subclass which enforces lower case storage in the
    database. It also compares case insensitively, e.g. abc123 == ABC123 == aBc123.
    """

    def get_prep_value(self, value):
        value = super().get_prep_value(value)
        return value if value is None else value.lower()


class AccountManager(models.Manager):
    def get_queryset(self):
        return super().get_queryset().filter(deleted_at__isnull=True)


class Account(models.Model):
    crsid = CRSIdField(primary_key=True, max_length=20)

    deleted_at = models.DateTimeField(null=True)
    valid_at = models.DateTimeField()
    updated_at = models.DateTimeField(auto_now=True)
    created_at = models.DateTimeField(auto_now_add=True)

    objects = AccountManager()
    all_objects = models.Manager()

    class Meta:
        constraints = [
            models.UniqueConstraint(Lower("crsid"), name="unique_lowercase_crsid"),
        ]


class AccountDetails(models.Model):
    account = models.OneToOneField(
        Account, related_name="account_details", on_delete=models.CASCADE, primary_key=True
    )
    terms_accepted = models.BooleanField(default=False, null=False)

    # These fields are taken as-is from Jackdaw, no additional special handling or constraints are
    # required. They are displayed to the user for confirmation, and no further processing is
    # needed. If the data from Jackdaw is the blank string this is acceptable.
    name = models.CharField(blank=True)
    affiliation = models.CharField(blank=True)
    college = models.CharField(blank=True)


class AccountIdentifier(models.Model):
    account_id = models.ForeignKey(
        Account, on_delete=models.CASCADE, related_name="account_identifier"
    )
    # Last name cannot have a case-insensitive collation as this prevents matching with a "LIKE"
    last_name = models.CharField(max_length=100)
    date_of_birth = models.DateField()
    code = models.CharField(max_length=100)

    class Meta:
        constraints = [
            models.UniqueConstraint(
                "last_name",
                "date_of_birth",
                "code",
                name="unique_account_identifier",
            ),
        ]


class Lockout(models.Model):
    identity_key = models.CharField(max_length=100)  # CRSId or Last Name
    date_of_birth = models.DateField()
    attempts = models.PositiveIntegerField(default=0)
    lockout_until = models.DateTimeField(null=True, blank=True)

    class Meta:
        constraints = [
            models.UniqueConstraint(
                fields=["identity_key", "date_of_birth"], name="unique_lockout"
            )
        ]