# global import, red is normal don't worry
import network_constants

import requests
import json
from typing import Dict, List
import logging

class TokenStash:
    '''
    used to keep track of already verified tokens in order to mitigate the traffic
    to the user-microservice
    '''
    trusted_tokens = {}
    roles = {}

    @staticmethod
    def add(token: str, username: str, role: str):
        '''
        adds a verified token to the stash
        '''
        TokenStash.trusted_tokens[token] = username
        TokenStash.roles[token] = role

    @staticmethod
    def is_token_cached(token: str) -> str:
        '''
        returns the associated username to a token, None otherwise
        '''

        if token in TokenStash.trusted_tokens and token in TokenStash.roles:
            return {"sub": TokenStash.trusted_tokens[token], "role": TokenStash.roles[token]}

        return None


def decodeToken(token: str, roles:List[str]=[]) -> Dict:
    '''
    verifies the passed token on the user-microservice and returns a dictionary with the
    subject entry if the verification was successful, an error is raised otherwise

    @params: 
            token - Required : JWT token from authorization header, must start with "Bearer "
            roles - Optional : User must have at least one of these roles
    '''

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

    cached_data = TokenStash.is_token_cached(token)
    if cached_data != None:
        # Re-Use cached token
        return cached_data

    url = f'https://{network_constants.REST_GATEWAY_HOSTNAME}:{network_constants.REST_GATEWAY_REST_PORT}/api/tokens/{token}'

    response = requests.post(
            url,
            verify=False, 
            proxies = { "http":None, "https":None }
            )

    if response.status_code != 200:
        raise ValueError(
            f"Validation of token failed ({response.status_code})!")

    data = json.loads(response.text)

    if not "username" in data or not "role" in data:
        raise ValueError(
            f"Validation of token failed (missing field in verification response)!")
    
    if len(roles) > 0 and data["role"] not in roles:
        raise ValueError(
            f"Validation of token failed (wrong role)!")

    TokenStash.add(token, data["username"], data["role"])
    return {"sub": data["username"], "role": data["role"]}

def _verify(token:str, roles:List[str]=[]):
    try:
        token_info =  decodeToken(token, roles=roles)
        return token_info
    except Exception as e:
        LOGGER = logging.getLogger(__name__)
        LOGGER.error("Token invalid: "+str(e))

        return None

def verifyTokenRegular(token, required_scopes):
    return _verify(token)

def verifyTokenAdmin(token, required_scopes):
    return _verify(token, roles=["a"])
