from django.db import models 
from django.utils.translation import gettext_lazy as _
from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin

from apps.core.models import BaseModel
from apps.accounts.managers import AccountManager

# Create your models here.

class RoleChoices(models.TextChoices):
    # Superuser with full access rights.
    ADMIN = 'ADMIN', _('Admin') 
    # Standard user of the platform.
    USER = 'USER', _('User') 
    # Guest user with limited access rights.
    GUEST = 'GUEST', _('Guest') 
    # Advertiser who creates and manages ad campaigns.
    ADVERTISER = 'ADVERTISER', _('Advertiser') 
    # Manager overseeing multiple ad campaigns and advertisers.
    MANAGER = 'MANAGER', _('Manager') 
    # Content creator responsible for crafting ad content.
    CONTENT_CREATOR = 'CONTENT_CREATOR', _('Content Creator') 
    # Analyst who evaluates the performance of ad campaigns.
    ANALYST = 'ANALYST', _('Analyst') 
    # Viewer who interacts with the ads on the platform.
    VIEWER = 'VIEWER', _('Viewer') 
    # Moderator who manages and oversees the content and interactions on the platform.
    MODERATOR = 'MODERATOR', _('Moderator')
    # Tech support personnel assisting users with platform-related issues.
    TECH_SUPPORT = 'TECH_SUPPORT', _('Tech Support')  
    # Customer service representative assisting users with inquiries and concerns.
    CUSTOMER_SERVICE = 'CUSTOMER_SERVICE', _('Customer Service')  
    # Business development executive focusing on platform growth and partnerships.
    BUSINESS_DEVELOPMENT = 'BUSINESS_DEVELOPMENT', _('Business Development')
        
class Account(AbstractBaseUser, BaseModel, PermissionsMixin):
    # # Unique identifier for each account using UUID.
    # id = models.UUIDField(
    #     primary_key=True, 
    #     default=uuid.uuid4,  # Generate a unique UUID for each new account.
    #     editable=False  # Prevent manual editing of the UUID.
    # ) 
    # First name of the user.
    first_name = models.CharField(
        _("First Name"), 
        max_length=255, 
        blank=True  # Can be left blank.
    ) 
    # Last name of the user.
    last_name = models.CharField(
        _("Last Name"), 
        max_length=255, 
        blank=True  # Can be left blank.
    ) 
    # Unique username for the user.
    username = models.CharField(
        _("Username"), 
        max_length=255, 
        blank=False,  # Must be provided.
        null=False,   # Cannot be NULL.
        unique=True   # Must be unique across accounts.
    ) 
    # Email address of the user.
    email = models.EmailField(
        _("Email Address"), 
        max_length=255,
        blank=False,  # Must be provided.
        null=False,   # Cannot be NULL.
        unique=True,  # Must be unique across accounts.
        error_messages={
            "unique": _("A user with that email already exists."),
        },
    ) 
    # Indicates if the user has superuser permissions.
    is_superuser = models.BooleanField(
        _("superuser status"),
        default=False,
        help_text=_("Designates that this user has all permissions without explicitly assigning them.")
    ) 
    # Indicates if the user has admin permissions.
    is_admin = models.BooleanField(
        _("Admin status"),
        default=False,
        help_text=_("Designates that this user has all staff permissions without explicitly assigning them.")
    ) 
    # Indicates if the account is active.
    is_active = models.BooleanField(
        _("active"),
        default=True,
        help_text=_("Designates whether this user should be treated as active. Unselect this instead of deleting accounts.")
    ) 
    # Indicates if the user is a staff member.
    is_staff = models.BooleanField(
        _("staff status"),
        default=False,
        help_text=_("Designates whether the user can log into this admin site.")
    ) 
    # Indicates if the user's account is verified.
    is_verified = models.BooleanField(
        _('Verified Account'),
        default=False
    ) 
    # Timestamp of the user's last login.
    last_login = models.DateTimeField(
        _("last login"), 
        null=True,   # Can be NULL.
        blank=True   # Can be left blank.
    ) 
    # Timestamp of when the user account was created.
    date_joined = models.DateTimeField(
        _("date joined"), 
        auto_now_add=True  # Automatically set to the current date and time when created.
    ) 
    # Role of the user within the platform.
    role = models.CharField(
        _("Role"),
        max_length=50,
        choices=RoleChoices.choices,  # Choices for the role field.
        default=RoleChoices.GUEST  # Default role for new accounts.
    )

    # Custom manager for the Account model.
    objects = AccountManager()  

    # Field used for authentication.
    USERNAME_FIELD = "email"  
    # Fields required when creating a user.
    REQUIRED_FIELDS = ["first_name", "last_name", "username"]

    class Meta:
        verbose_name = _("Account")  # Singular name for the model in the admin interface.
        verbose_name_plural = _("Accounts")  # Plural name for the model in the admin interface.
        ordering = ["-date_joined"]  # Default ordering of records.

    def __str__(self):
        """
        Return the string representation of the model instance, which is typically the email address.
        
        This method provides a human-readable string representation of the model instance. 
        For the Account model, it returns the email address as the primary identifier.
        
        Returns:
        - str: Email address of the user for string representation.
        """
        
        # Return the email address as the string representation of the model instance.
        return self.email

    @property
    def full_name(self):
        """
        Retrieve the full name of the user by concatenating the first name and last name, 
        or return a default string if both names are not provided.
        
        This property attempts to construct the full name of the user by combining the 
        first name and last name fields. If either the first name or last name is not 
        provided or empty, it constructs a default full name using the first 8 characters 
        of the user's unique identifier (UUID).
        
        Returns:
        - str: Full name of the user or a default string.
        """
        # Check if both first name and last name are provided; if so, return the full name.If either first name or last name is not provided, construct a default full name using the first 8 characters of the UUID.
        return f"{self.first_name} {self.last_name}" if self.first_name or self.last_name else f"User-{str(self.id)[:8]}"
    
    def get_short_name(self):
        """
        Retrieve the short name of the user, typically the first name, or a default string 
        if the first name is not provided.
        
        This method returns the first name of the user if available. If the first name is not 
        provided or empty, it constructs a default short name using the first 8 characters 
        of the user's unique identifier (UUID).
        
        Returns:
        - str: Short name of the user or a default string.
        """
        # Check if the user has a first name; if so, return it. If the first name is not provided, construct a default short name using the first 8 characters of the UUID.
        return self.first_name if self.first_name else f"User-{str(self.id)[:8]}"

    def check_password(self, raw_password):
        """
        Check the given raw password against the encrypted password stored in the database.
        
        This method uses Django's built-in password hasher to securely compare the raw password 
        with the hashed password stored in the database. If they match, it returns True; otherwise, 
        it returns False.
        
        Parameters:
        - raw_password (str): The raw password entered by the user attempting to authenticate.
        
        Returns:
        - bool: True if the raw password matches the stored hashed password, False otherwise.
        """ 
        # Import Django's check_password function for comparing passwords.
        from django.contrib.auth.hashers import check_password 
        # Use Django's check_password function to verify the raw password against the stored hashed password.
        return check_password(raw_password, self.password)
    
    def save(self, *args, **kwargs):
        """
        Save the model instance and ensure that the user is marked as an admin 
        if they have superuser permissions.
        
        This method overrides the default save behavior to ensure consistency in the 
        'is_admin' field based on the 'is_superuser' field. If a user has superuser permissions, 
        they will also be marked as an admin. This ensures that superusers implicitly have 
        admin privileges without explicitly setting the 'is_admin' field.
        
        Returns:
        - Model instance: The saved model instance.
        """ 
        # Ensure that the user is marked as an admin if they have superuser permissions.
        self.is_admin = self.is_admin or self.is_superuser
        # Call the save method of the parent class to save the model instance.
        return super().save(*args, **kwargs)

    def clean(self):
        """
        Clean method to perform validation and data cleaning operations before saving the model instance.
        
        This method ensures that before saving the model instance, the email address is normalized 
        to maintain consistency and avoid duplicate or inconsistent email entries. 
        The email normalization process standardizes the email format by converting it to lowercase.
        
        Note:
        - The super().clean() method is called to perform any additional validation or cleaning operations 
        defined in the parent class.
        
        Returns:
        - None: The method modifies the 'email' attribute of the model instance in place.
        """ 
        # Call the clean method of the parent class to perform any additional validation or cleaning operations.
        super().clean() 
        # Normalize the email address by converting it to lowercase for consistency.
        self.email = self.__class__.objects.normalize_email(self.email)