# global imports (dont't worry, red is normal)
from db.repository import Repository
from db.entities.user import User

from datetime import datetime
import bcrypt

class UserService:
    _repository = Repository()
    # u ... regular user
    # a ... admin user
    _valid_roles = ["u", "a"]

    @staticmethod
    def update(user: User):
        UserService._repository.update(user)
    
    @staticmethod
    def get_by_username(username: str):
        '''
        fetches the given user from the database
        throws a ValueError if the user does not exist
        @params:
            username - Required : string identifier for the user i.e. an email address
        '''
        user = UserService._repository.one_by_username(username)

        # return 400 if the user does not exist
        if len(user) == 0:
            raise ValueError(f'User with username "{username}" does not exist')

        return User.from_serializable_dict(user[0])

    @staticmethod
    def get_by_credentials(username, password):
        '''
        fetches the given user from the database and checks if the password matches the stored one
        throws a ValueError if the user does not exist or the password is wrong
        @params:
            username - Required : string identifier for the user i.e. an email address
            password - Required : passphrase used to authenticate later, raw plaintext
        '''
        user = UserService.get_by_username(username)
        
        hashed_password = user.password

        if not bcrypt.checkpw(password.encode("utf-8"), hashed_password.encode("utf-8")):
            raise ValueError(f'Wrong credentials for user "{username}"')
        
        return user
        
    @staticmethod
    def delete(username):
        '''
        deletes the given user from the database
        throws a ValueError if the user does not exist
        @params:
            username - Required : string identifier for the user i.e. an email address
        '''

        reference_users = UserService._repository.one_by_username(username)

        # return 400 if the user does not exist
        if len(reference_users) == 0:
            raise ValueError(f'User with username "{username}" does not exist')
        
        UserService._repository.delete_all_with_username(username)

    @staticmethod
    def add(username, password, role="u"):
        '''
        adds the given user to the database
        throws a ValueError if the user already exists
        @params:
            username - Required : string identifier for the user i.e. an email address
            password - Required : passphrase used to authenticate later, raw plaintext
            role     - Optional : user type, one of the following: [u=regular user (default)]   
        '''

        if role not in UserService._valid_roles:
            raise ValueError(f'Role "{role}" is invalid. Must be one of {UserService._valid_roles}')

        reference_users = UserService._repository.one_by_username(username)

        if len(reference_users) > 0:
            raise ValueError(f'User with username "{username}" already exists')

        created_at = str(datetime.now())
        last_login = str(datetime.min)

        # hash the password using the BCrypt algorithm, which generates a string that
        # contains the algorithm, the salt and the hash
        password = bcrypt.hashpw(password.encode("utf-8"), bcrypt.gensalt()).decode("utf-8")

        user_new = User(username, password, role=role)
        user_new.created_at = created_at
        user_new.last_login = last_login

        UserService._repository.add(user_new)

        