import os

import msal

from .configuration import api_config
from .token_cache import token_cache

API_APP_ID_URI_BASE = f"api://{api_config.aad_app_client_id}"
SCOPES = [
    f"{API_APP_ID_URI_BASE}/.default",
]


class NoneAuth(Exception):
    """User not logged in. Please log in using `arcl auth login <organization_id>"""

    pass


class ArchimedesPublicClientAuth:
    def __init__(self, client_id, authority, cache=None):
        self.app = self.build_msal_app(client_id, authority, cache=cache)

    @staticmethod
    def build_msal_app(client_id, authority, cache=None):
        return msal.PublicClientApplication(
            client_id,
            authority=authority,
            token_cache=cache,
        )


class ArchimedesLocalAuth(ArchimedesPublicClientAuth):
    def __init__(self):
        super().__init__(api_config.client_id, api_config.authority, token_cache)

    def get_access_token_silent(self):
        # We now check the cache to see
        # whether we already have some accounts that the end user already used to sign in before.
        accounts = self.app.get_accounts()
        if not accounts:
            return None

        chosen = accounts[0]
        result = self.app.acquire_token_silent(SCOPES, account=chosen)

        if result is None or "access_token" not in result:
            description = f" Error details: {result['error_description']}" if "error_description" in result else ""
            raise NoneAuth(
                f"User not logged in. Please log in using `arcl auth login <organization_id>`.{description}"
            )

        return result.get("access_token")


class ArchimedesConfidentialAuth:
    def __init__(self, client_id, client_credential, authority):
        self.app = self.build_confidential_msal_app(client_id, client_credential, authority)

    def get_access_token_silent(self):
        result = self.app.acquire_token_for_client(SCOPES)

        if result is None or "access_token" not in result:
            description = f" Error details: {result['error_description']}" if "error_description" in result else ""
            raise NoneAuth(
                "Authentication failed. Please make sure that AZURE_AD_APP_ID, AZURE_AD_APP_CLIENT_CREDENTIAL and "
                f"AZURE_AD_TENANT_ID are properly configured.{description}"
            )

        return result.get("access_token")

    @staticmethod
    def build_confidential_msal_app(client_id, client_credential, authority):
        return msal.ConfidentialClientApplication(
            client_id=client_id,
            client_credential=client_credential,
            authority=authority
        )


archimedes_auth = None
use_app_authentication = os.getenv("USE_APP_AUTHENTICATION", "false").strip().lower() not in ["false", "f", "0"]
use_web_authentication = os.getenv("USE_WEB_AUTHENTICATION", "false").strip().lower() not in ["false", "f", "0"]
assert not (use_app_authentication and use_web_authentication), \
    "Only one of USE_APP_AUTHENTICATION or USE_WEB_AUTHENTICATION can be set to TRUE"
is_local = not use_app_authentication and not use_web_authentication
if is_local:
    archimedes_auth = ArchimedesLocalAuth()
else:
    azure_ad_tenant_id = os.getenv("AZURE_AD_TENANT_ID")
    azure_ad_client_id = os.getenv("AZURE_AD_APP_ID")
    azure_ad_authority = f"https://login.microsoftonline.com/{azure_ad_tenant_id}"

    if use_app_authentication:
        azure_ad_client_credential = os.getenv("AZURE_AD_APP_CLIENT_CREDENTIAL")
        archimedes_auth = ArchimedesConfidentialAuth(azure_ad_client_id, azure_ad_client_credential, azure_ad_authority)
    elif use_web_authentication:
        archimedes_auth = None
