import uuid
from datetime import datetime
from typing import Optional, Tuple

from nacl import pwhash
from nacl.exceptions import InvalidkeyError

from atheneum.model import User, UserToken
from atheneum.service import user_service, user_token_service


def generate_token() -> uuid.UUID:
    return uuid.uuid4()


def get_password_hash(password: str) -> Tuple[str, int]:
    """
    Retrieve argon2id password hash.

    :param password: plaintext password to convert
    :return: Tuple[password_hash, password_revision]
    """
    return pwhash.argon2id.str(password.encode('utf8')).decode('utf8'), 1


def is_valid_password(user: User, password: str) -> bool:
    assert user

    try:
        return pwhash.verify(
            user.password_hash.encode('utf8'), password.encode('utf8'))
    except InvalidkeyError:
        pass
    return False


def is_valid_token(user_token: Optional[UserToken]) -> bool:
    """
    Token must be enabled and if it has an expiration, it must be
    greater than now.

    :param user_token:
    :return:
    """
    if user_token is None:
        return False
    if not user_token.enabled:
        return False
    if (user_token.expiration_time is not None
            and user_token.expiration_time < datetime.utcnow()):
        return False
    return True


def bump_login(user: Optional[User]) -> None:
    """
    Update the last login time for the user

    :param user:
    :return:
    """
    if user is not None:
        user_service.update_last_login_time(user)


def logout(user_token: Optional[UserToken] = None) -> None:
    """
    Remove a user_token associated with a client session

    :param user_token:
    :return:
    """
    if user_token is not None:
        user_token_service.delete(user_token)