from datetime import datetime
from mongoengine import StringField, EmailField, DateTimeField, BooleanField, ListField, ReferenceField, CASCADE, EmbeddedDocumentField


from app.config.extensions import bcrypt

from app.models.base import TimestampedMixin
from app.models.role import Role
from app.models.profile import Profile
 
class User(TimestampedMixin):
    """
    Professional User model for managing user information.

    Attributes:
        username (str): The unique username of the user (required).
        email (str): The email address of the user (required, unique).
        password (str): The user's password (required).
        first_name (str): The user's first name.
        last_name (str): The user's last name. 
        is_verified (bool): A flag indicating if the user's email is verified.
        is_superuser (bool): A flag indicating if the user is a superuser.
        is_admin (bool): A flag indicating if the user is an admin.
        is_active (bool): A flag indicating if the user account is active.
        is_staff (bool): A flag indicating if the user is staff.
        last_login (datetime): The date and time of the user's last login.
        date_joined (datetime): The date and time of user registration (auto-generated).
        roles (list of Role): User roles or permissions (related to Role model).
        last_seen_at (datetime): The date and time when the user was last seen.
        notif_enabled (bool): A flag indicating if notifications are enabled for the user.
        is_online (bool): A flag indicating if the user is currently online.
        is_blocked (bool): A flag indicating if the user is blocked.

    Methods:
        __str__(): Return the username as a string representation of the user.
        set_password(password: str): Set the user's password (typically after hashing).
        check_password(password: str): Check if the provided password matches the user's password.
        add_role(role: Role): Add a role to the user's list of roles.
        remove_role(role: Role): Remove a role from the user's list of roles.
        mark_as_online(): Mark the user as online.
        mark_as_offline(): Mark the user as offline.
        block_user(): Block the user, preventing certain actions.
        unblock_user(): Unblock the user, allowing them to resume normal activities.
        update_last_seen(): Update the user's last seen timestamp to the current time.

    """

    username     = StringField( 
        max_length=50, 
        help_text="The unique username of the user."
    )
    email        = EmailField(
        required=True, 
        unique=True, 
        help_text="The email address of the user."
    )
    password     = StringField(
        required=True, 
        help_text="The user's password."
    )

    company_name = StringField(
        max_length=150, 
        help_text="The user's Company name."
    )
    company_type = StringField(
        required=True,  
        max_length=150, 
        choices=["Anonymous Company", "Brands Company", "Ad Agency Company", "TV Channels Company"],
        help_text="The user's Company type."
    )
    
    is_verified  = BooleanField(
        default=False, 
        help_text="A flag indicating if the user's email is verified."
    )
    is_superuser = BooleanField(
        default=False, 
        help_text="A flag indicating if the user is a superuser."
    )
    is_admin     = BooleanField(
        default=False, 
        help_text="A flag indicating if the user is an admin."
    )
    is_active    = BooleanField(
        default=True, 
        help_text="A flag indicating if the user account is active."
    )
    is_staff     = BooleanField(
        default=False, 
        help_text="A flag indicating if the user is staff."
    )
    is_online    = BooleanField(
        default=False, 
        help_text="A flag indicating if the user is currently online."
    )
    is_blocked   = BooleanField(
        default=False, 
        help_text="A flag indicating if the user is blocked."
    ) 
    notif_enabled = BooleanField(
        default=True, 
        help_text="A flag indicating if notifications are enabled for the user."
    )

    last_login   = DateTimeField(
        default=None, 
        help_text="The date and time of the user's last login."
    )
    date_joined  = DateTimeField(
        default=datetime.utcnow, 
        readonly=True, 
        help_text="The date and time of user registration"
    )
    last_seen_at = DateTimeField(
        default=None, 
        help_text="The date and time when the user was last seen."
    )
    
    profile      = EmbeddedDocumentField(
        Profile, 
        help_text="User profile information."
    )
    role         = ReferenceField(
        Role, 
        reverse_delete_rule=CASCADE, 
        help_text="User roles or permissions (related to Role model)."
    ) 

    
    def __str__(self):
        """
        Return the username as a string representation of the user.

        Returns:
            str: The username of the user.
        """
        return self.username

    def set_password(self, password: str):
        """
        Set the user's password, typically after hashing it.

        Args:
            password (str): The user's password.

        Returns:
            None
        """
        self.password = bcrypt.generate_password_hash(password).decode('utf-8')

    def check_password(self, password: str):
        """
        Check if the provided password matches the user's password.

        Args:
            password (str): The password to check.

        Returns:
            bool: True if the password matches, False otherwise.
        """
        return bcrypt.check_password_hash(self.password, password)  

    def add_role(self, role):
        """
        Add a role to the user's list of roles.

        Args:
            role (Role): The role to add.

        Returns:
            None
        """
        
        if role not in self.roles:
            new_role = Role(name=role)
            new_role.save()
            
            self.roles.append(new_role)
            self.save()
                
            

    def remove_role(self, role):
        """
        Remove a role from the user's list of roles.

        Args:
            role (Role): The role to remove.

        Returns:
            None
        """
        
        if role in self.roles:
            self.roles.remove(role)

    def ensure_client_role(self):
        if not self.role:
            client_role = Role.objects(name="admin").first()
            if not client_role:
                # If the "client" role doesn't exist, create it.
                client_role = Role(name="admin")
                client_role.save()

            self.role = client_role
            self.save()

    def toggle_active_status(self):
        """
        Toggle the active status of the user.

        If the user is currently active, this function will deactivate the user.
        If the user is currently inactive, this function will activate the user.
        """
        self.is_active = not self.is_active
        self.save()

    def mark_as_online(self):
        """
        Mark the user as online.

        Returns:
            None
        """
        self.is_online = True

    def mark_as_offline(self):
        """
        Mark the user as offline.

        Returns:
            None
        """
        self.is_online = False

    def block_user(self):
        """
        Block the user, preventing them from certain actions.

        Returns:
            None
        """
        self.is_blocked = True

    def unblock_user(self):
        """
        Unblock the user, allowing them to resume normal activities.

        Returns:
            None
        """
        self.is_blocked = False

    def update_last_seen(self):
        """
        Update the user's last seen timestamp to the current time.

        Returns:
            None
        """
        self.last_seen_at = datetime.utcnow()

    def update_last_login(self):
        """
        Update the user's last login timestamp to the current time.

        Returns:
            None
        """
        self.last_login = datetime.utcnow()

    def to_dict(self):
        """
        Convert the document to a dictionary.
        """ 
        user_dict = super(User, self).to_dict()
        
        user_dict.pop('id', None) 
        user_dict.pop('password', None)
        user_dict.pop('is_online', None)

        if not user_dict.get('deleted', False):
            user_dict.pop('deleted', None) 
            user_dict.pop('deleted_at', None)

        user_dict['date_joined'] = self._format_datetime(user_dict.get('date_joined'))
        user_dict['last_login'] = self._format_datetime(user_dict.get('last_login'))
        user_dict['last_seen_at'] = self._format_datetime(user_dict.get('last_seen_at')) 
        
        user_dict.pop('_cls', None)
        
        # Convert the roles to a list of role names
        user_dict['role'] =  self.role.to_dict()

        return user_dict