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

import jwt
from datetime import datetime, timedelta
from typing import Dict

SIGNING_KEY = "yteNrMy6142WKwp8fKfrHkS5nlFpxtHgOXJh1ZPsOrV_gTcsO9eMY7aB7HUzRbTRO9dmZhCl3FdPtuvMe3K8aBA_wc2MmHRo8IkUIGmvUJGsAxKFClN_6oNW5fEvoeVKiL1krA-qjWbR_em-WksePgPoTsySW7QbKdi4f7cwuyK2_JZ2fQj9hDKlfJ2GzMXkKiWcfyCTr30yC6BviAFeRDD_Bpvg6znsrXr53Tq66hnwDwQ6QU7aHVu-bERblKZTYuvkSxsov6yRMEVWQoiuBITsQtIOcgSWK4Dy3BjSbqoIcKw3WG-s3wx1lTen19QbEu8vJC64e0iGeGDWT6vbtg"
TOKEN_VALIDITY_IN_DAYS = 1

def verifyTokenRegular(token, required_scopes) -> Dict:
    try:
        user = TokenService.verify(token)
    except ValueError as e:
        print(f'ERROR DURING TOKEN VALIDATION: {str(e)}')
        return None
    
    if not user.role in UserService._valid_roles:
        return None

    TokenService.successful_authentication(user)
    return {"sub": user.username}

def verifyTokenAdmin(token, required_scopes) -> Dict:
    try:
        user = TokenService.verify(token)
    except ValueError as e:
        print(f'ERROR DURING TOKEN VALIDATION: {str(e)}')
        return None
    
    if not user.role == "a":
        print(f"Required Role:'a', Provided Role: '{user.role}'")
        return None

    TokenService.successful_authentication(user)
    return {"sub": user.username}

class TokenService:
    @staticmethod
    def successful_authentication(user: User):
        user.last_login = str(datetime.now())
        UserService.update(user)

    @staticmethod
    def generate_token(user: User) -> str:
        '''
        creates a JWT token for a user which has the following fields:
            - username
            - created_at
            - valid_until
        '''
        created_at = datetime.now()
        valid_until = created_at + timedelta(days=1)
        return jwt.encode(
            {
                'username': user.username, 
                'created_at': str(created_at), 
                'valid_until': str(valid_until),
            }, 
            SIGNING_KEY, algorithm='HS256'
        ).decode("utf-8")

    @staticmethod
    def verify(token : str, **kwargs) -> User:
        '''
        verifies the validity of a JWT token. Raises a ValueError if one of the tests failes
        performs the following tests (int this order):
            - is the JWT token parsable? (it has not been damaged + the signature is valid)
            - does the payload contain all necessary fields?
            - does the user specified by the payload exist?
            - is the expiration/creation date sound?
        @params:
            token - Required : JWT token from authorization header, must start with "Bearer "
        '''

        if not token.startswith("Bearer "):
            raise ValueError('Invalid JWT token (must be a Bearer string)')

        token = token[7:]

        try:
            payload = jwt.decode(token, SIGNING_KEY, algorithms=['HS256'])
        except:
            raise ValueError('Invalid JWT token (decoding failed)')

        # check if all needed fields are in the payload
        if not "username" in payload or not "created_at" in payload or not "valid_until" in payload:
            return 'Invalid JWT token (missing fields)'
        
        user = UserService.get_by_username(payload["username"])

        # check if token has already expired
        token_created_at = datetime.strptime(payload["created_at"], '%Y-%m-%d %H:%M:%S.%f')
        valid_until = datetime.strptime(payload["valid_until"], '%Y-%m-%d %H:%M:%S.%f')
        now = datetime.now()

        if now <= token_created_at or now >= valid_until:
            raise ValueError('Invalid JWT token (token expired)')
        
        return user