from __future__ import annotations

from django.contrib.auth.models import AbstractUser
from django.db import models
from django.utils import timezone
from django.contrib.auth.hashers import check_password, make_password


class CustomUser(AbstractUser):
    class Role(models.TextChoices):
        SUPERUSER = "Superuser", "Superuser"
        ENGINEERS = "Engineers", "Engineers"
        ACCOUNTANT = "Accountant", "Accountant"
        PROJECT_MANAGER = "Project_Manager", "Project Manager"
        UPLOADER = "Uploader", "Uploader"

    email = models.EmailField(unique=True)
    role = models.CharField(max_length=32, choices=Role.choices, default=Role.ENGINEERS)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ["username"]
        indexes = [
            models.Index(fields=["email"]),
            models.Index(fields=["role"]),
            models.Index(fields=["created_at"]),
        ]

    def __str__(self) -> str:
        return f"{self.username} ({self.role})"


class LoginOTP(models.Model):
    user = models.ForeignKey(CustomUser, on_delete=models.CASCADE, related_name="login_otps")
    code_hash = models.CharField(max_length=128)
    expires_at = models.DateTimeField()
    is_used = models.BooleanField(default=False)
    attempts = models.PositiveSmallIntegerField(default=0)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ["-created_at"]
        indexes = [
            models.Index(fields=["user", "is_used", "expires_at"]),
            models.Index(fields=["created_at"]),
        ]

    @property
    def is_expired(self) -> bool:
        return timezone.now() >= self.expires_at

    def mark_used(self) -> None:
        self.is_used = True
        self.save(update_fields=["is_used", "updated_at"])

    def validate(self, raw_code: str) -> bool:
        return not self.is_used and not self.is_expired and check_password(raw_code, self.code_hash)

    @classmethod
    def create_for_user(cls, user: CustomUser, raw_code: str, expires_at):
        cls.objects.filter(user=user, is_used=False).update(is_used=True)
        return cls.objects.create(
            user=user,
            code_hash=make_password(raw_code),
            expires_at=expires_at,
        )

    def __str__(self) -> str:
        return f"OTP for {self.user.username} ({'used' if self.is_used else 'active'})"
