import random

from django.conf import settings
from django.contrib.auth.models import BaseUserManager
from django.core.exceptions import ObjectDoesNotExist
from django.db import models
from django.utils import timezone

from apps.account.services.mail_services import send_opt_verification_mail


class UserManager(BaseUserManager):
    """
    Custom manager for the User model.

    Methods:
        create_user(email, password, **extra_fields): Creates and saves a regular user.
        create_superuser(email, password, **extra_fields): Creates and saves a superuser.
        lowercase_email(email): Converts an email address to lowercase.
    """

    def _create_user(self, email, password, **extra_fields):
        """
        Creates and saves a User with the given email and password.

        Args:
        email (str): The user's email address.
        password (str): The user's password.
        **extra_fields: Additional fields for the user.

        Returns:
        User: The created User instance.

        This method is used to create and save a regular user with the given email and password.
        """

        email = self.lowercase_email(email)
        user = self.model(email=email, **extra_fields)
        user.set_password(password)
        user.save(using=self._db)
        return user

    def create_user(self, email=None, password=None, **extra_fields):
        """
        Creates and saves a regular user.

        Args:
        email (str): The user's email address.
        password (str): The user's password.
        **extra_fields: Additional fields for the user.

        Returns:
        User: The created regular User instance.

        This method is used to create and save a regular user with the given email, password, and optional
        extra fields. By default, regular users are not staff members or superusers.
        """

        extra_fields.setdefault("is_staff", False)
        extra_fields.setdefault("is_superuser", False)
        return self._create_user(email, password, **extra_fields)

    def create_superuser(self, email=None, password=None, **extra_fields):
        """
        Creates and saves a superuser.

        Args:
        email (str): The superuser's email address.
        password (str): The superuser's password.
        **extra_fields: Additional fields for the superuser.

        Returns:
        User: The created superuser User instance.

        This method is used to create and save a superuser with the given email, password, and optional
        extra fields. Superusers have staff and superuser privileges.
        """

        extra_fields.setdefault("is_staff", True)
        extra_fields.setdefault("is_superuser", True)

        if extra_fields.get("is_staff") is not True:
            raise ValueError("Superuser must have is_staff=True.")
        if extra_fields.get("is_superuser") is not True:
            raise ValueError("Superuser must have is_superuser=True.")

        email = self.lowercase_email(email)
        return self._create_user(email, password, **extra_fields)

    def get_queryset(self):
        """
        Override the default queryset to filter out removed users.

        Returns:
        QuerySet: The filtered queryset of non-removed users.

        This method overrides the default queryset to filter out users who have been removed (soft deleted).
        """
        return super().get_queryset().filter(is_removed=False)

    @staticmethod
    def lowercase_email(email):
        """
        Convert an email address to lowercase.

        Args:
        email (str): The email address to convert.

        Returns:
        str: The lowercase email address.

        This method is used to convert an email address to lowercase to
        ensure case-insensitive matching when users log in.
        """
        email = email or ""
        try:
            email = email.strip().lower()
        except ValueError:
            pass
        return email


class UserCodeManager(models.Manager):
    """
    Custom manager for the UserCode model.

    Methods:
        create_or_update_code(user): Create or update a UserCode instance for the given user.
    """

    def create_or_update_code(self, user):
        """
        Create or update a UserCode instance for the given user.

        Args:
        user (User): The user for whom the verification code is created or updated.

        Returns:
        UserCode: The UserCode instance associated with the user.

        This method is used to create or update a UserCode instance for a user. It generates
        a verification code and updates the timestamp.
        """

        # Get the current timestamp
        date_updated = timezone.now()

        try:
            # Try to retrieve a UserCode instance for the given use
            user_code = self.get(
                user=user,
            )
        except ObjectDoesNotExist:
            # If the UserCode doesn't exist, create a new one
            user_code = self.create(user=user, code=123456)

        if user_code:
            # If a UserCode instance exists, update its code and timestamp
            if settings.IS_PRODUCTION:
                # In production, generate a random 6-digit code
                code_value = "".join(random.choices("1234567890", k=6))
                user_code.code = code_value
            else:
                user_code.code = 123456

            # Update the timestamp and save the UserCode instance
            user_code.timestamp = date_updated
            user_code.save()

        # send_opt_verification_mail(user_code)

        return user_code


class UserVehicleManager(BaseUserManager):
    """
    Custom manager for the UserVehicle model.

    Methods:
        get_queryset(): Override the default queryset to filter out removed users.
    """

    def get_queryset(self):
        """
        Override the default queryset to filter out removed users.

        Returns:
        QuerySet: The filtered queryset of user vehicle records.

        This method is used to customize the default queryset for the UserVehicle model.
        It filters out user vehicle records associated with users marked as removed (is_removed=True).
        """
        return super().get_queryset().filter(is_removed=False)


class UserVehicleAssetManager(BaseUserManager):
    """
    Custom manager for the UserVehicleAsset model.

    This manager provides additional methods for querying UserVehicleAsset instances.

    Methods:
        get_queryset: Returns a queryset of UserVehicleAsset instances with 'is_removed' set to False.
    """

    def get_queryset(self):
        """
        Get a queryset of UserVehicleAsset instances with 'is_removed' set to False.

        Returns:
            QuerySet: The queryset of UserVehicleAsset instances.
        """
        return super().get_queryset().filter(is_removed=False)


class ContactUsManager(BaseUserManager):
    """
    Custom manager for the ContactUs model.

    This manager provides additional methods for querying ContactUs instances.

    Methods:
        get_queryset: Returns a queryset of ContactUs instances with 'is_removed' set to False.
    """

    def get_queryset(self):
        """
        Get a queryset of ContactUs instances with 'is_removed' set to False.

        Returns:
            QuerySet: The queryset of ContactUs instances.
        """
        return super().get_queryset().filter(is_removed=False)


class ContactUsAssetsManager(BaseUserManager):
    """
    Custom manager for the ContactUsAssets model.

    This manager provides additional methods for querying ContactUsAssets instances.

    Methods:
        get_queryset: Returns a queryset of ContactUsAssets instances with 'is_removed' set to False.
    """

    def get_queryset(self):
        """
        Get a queryset of ContactUsAssets instances with 'is_removed' set to False.

        Returns:
            QuerySet: The queryset of ContactUsAssets instances.
        """
        return super().get_queryset().filter(is_removed=False)


class NewsLetterSubscriberManager(BaseUserManager):
    """
    Custom manager for the NewsLetterSubscriber model.

    This manager provides additional methods for querying NewsLetterSubscriber instances.

    Methods:
        get_queryset: Returns a queryset of NewsLetterSubscriber instances with 'is_removed' set to False.
    """

    def get_queryset(self):
        """
        Get a queryset of NewsLetterSubscriber instances with 'is_removed' set to False.

        Returns:
            QuerySet: The queryset of NewsLetterSubscriber instances.
        """
        return super().get_queryset().filter(is_removed=False)
