from adafri.cache.lib import cache
from adafri.utils import ApiResponse, ArrayUtils, Error, ResponseStatus, StatusCode
from adafri.v1.account import Account
from authlib.integrations.flask_client import OAuth
from authlib.integrations.flask_oauth2 import AuthorizationServer, ResourceProtector
from authlib.oauth2.rfc7636 import CodeChallenge
from authlib.oauth2.rfc6749 import grants as _grants

from ...oauth import JwtTokenGenerator, JwtTokenValidator, OAuthClient, OAuthToken
from ...oauth import OpenIDAuthorizationCodeGrant as AuthCodeGrant
from ...oauth import OpenIDCode, OpenIDHybridGrant
from ...oauth.models.token import TokenIntrospectionEndpoint, RefreshTokenGrant
from .jwks import jwks_manager

oidc_authorization_server = AuthorizationServer()
require_oidc_oauth = ResourceProtector()
import json
import os

import requests

flask_oauth = OAuth();
provider = None;

def get_adafri_provider_cfg(base=None):
    if base is not None:
        discovery_url = base + "/.well-known/openid-configuration"
        return requests.get(discovery_url).json()
    return None

def config_oidc_oauth(app, query_client=None, save_token=None, token_generators=[]):
    flask_oauth.init_app(app)
    if query_client is None:
        query_client = OAuthClient().get_by_client_id
    if save_token is None:
        save_token = OAuthToken().save
    oidc_authorization_server.query_client = query_client
    oidc_authorization_server.save_token = save_token
    oidc_authorization_server.init_app(app)
    oidc_authorization_server.register_grant(AuthCodeGrant, [
        OpenIDCode(require_nonce=False),
        CodeChallenge(required=True)
    ])
    # oidc_authorization_server.register_grant(OpenIDImplicitGrant)
    oidc_authorization_server.register_grant(OpenIDHybridGrant)
    oidc_authorization_server.register_grant(RefreshTokenGrant)
    oidc_authorization_server.register_grant(_grants.ClientCredentialsGrant)
    oidc_authorization_server.register_endpoint(TokenIntrospectionEndpoint)
    oidc_authorization_server.register_token_generator("default", JwtTokenGenerator(issuer=os.getenv('JWT_ISSUER')))
    oidc_authorization_server.register_token_generator("client_credentials", JwtTokenGenerator(issuer=os.getenv('JWT_ISSUER')))
    require_oidc_oauth.register_token_validator(JwtTokenValidator(issuer=os.getenv('JWT_ISSUER'),
        resource_server=os.getenv('JWT_ISSUER')))
        
def guards_oidc(scopes=['profile'], request=None):
    """
    A decorator that checks if the user has the required scopes to access this route.

    Args:
        scopes (list): A list of scopes that the user must have to access this route.
        Defaults to ['profile'].

    Returns:
        The user's token if the user has the required scopes, otherwise None.
    """
    with require_oidc_oauth.acquire(scopes=scopes) as token:
        return token
    
def get_admins():
    admins = []
    try:
        strings = os.getenv('ADMIN_USERS')
        if strings is None:
            return []
        admins = json.loads(strings)
    except:
        admins = []
    return admins

def is_admin(uid: str):
    admins = get_admins()
    if bool(admins) is True and uid in admins:
        return ApiResponse(ResponseStatus.OK, StatusCode.status_200, None, None);
    return ApiResponse(ResponseStatus.ERROR, StatusCode.status_401, None, Error("PERMISSION DENIED","INVALID_REQUEST", 1))
def guard_account(accountId: str, uid: str):
    admins = get_admins()
    account = cache.getCache(accountId, Account)
    if account is None:
        account = Account(account={"id": accountId}).get()
        if account is not None:
            cache.set(accountId, account.to_dict(), 225)
    if account is None:
        return ApiResponse(ResponseStatus.ERROR, StatusCode.status_401, None, Error("PERMISSION DENIED","INVALID_REQUEST", 1))
    if bool(admins) is True and uid in admins:
        return ApiResponse(ResponseStatus.OK, StatusCode.status_200, account, None);
    # account = ArrayUtils.find(accounts, lambda x: x.id == accountId)
    if account.owner != uid:
        links_cache = cache.get(f"links-{uid}")
        if links_cache is not None:
            links = Account().fromListDict(links_cache)
        else:
            links = Account(account=None).getUserAccountsLinked(uid)
            cache.set(f"links-{uid}", links, 225)
        # links = Account(account=None).getUserAccountsLinked(uid)
        # print('links', links)
        account = ArrayUtils.find(links, lambda x: x.id==accountId)
        if account is None:
            return ApiResponse(ResponseStatus.ERROR, StatusCode.status_401, None, Error("PERMISSION DENIED","INVALID_REQUEST", 1))
    return ApiResponse(ResponseStatus.OK, StatusCode.status_200, account, None);