# coding: utf-8
from fastapi import Depends, Security  # noqa: F401
from fastapi.openapi.models import OAuthFlowImplicit, OAuthFlows  # noqa: F401
from fastapi.security import (  # noqa: F401
    HTTPAuthorizationCredentials,
    HTTPBasic,
    HTTPBasicCredentials,
    HTTPBearer,
    OAuth2,
    OAuth2AuthorizationCodeBearer,
    OAuth2PasswordBearer,
    SecurityScopes,
)
from fastapi import status, HTTPException  # noqa: F401
from fastapi.security.api_key import APIKeyCookie, APIKeyHeader, APIKeyQuery  # noqa: F401
from server.apis.authentication_api import ErrorTypes, Messages, hasSqlInjectionCharacters
from server.data.definitions import Groups
from server.models.error import Error
from server.models.extra_models import TokenModel
from server.apis import sqliteClient


def get_token_ApiKeyAuth(
    token_api_key_header: str = Security(APIKeyHeader(name="X-API-TOKEN", auto_error=False))
) -> TokenModel:
    """
    Check and retrieve authentication information from api_key.

    :param token_api_key_header: API key provided by Authorization[X-API-TOKEN] header
    :type token_api_key_header: str
    :return: Information attached to provided api_key or None if api_key is invalid or does not allow access to called API
    :rtype: TokenModel | None
    """

    # -------------------------------------------- Check fields aren't empty ------------------------------------------

    if token_api_key_header is None:
        errorResponse = Error(
            status=status.HTTP_400_BAD_REQUEST,
            type=ErrorTypes.TOKEN_MISSING.value,
            message=Messages.TOKEN_MISSING.value,
        )
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=errorResponse.dict())

    # -------------------------------------------- Injection test --------------------------------------------

    if hasSqlInjectionCharacters(token_api_key_header):
        errorResponse = Error(
            status=status.HTTP_400_BAD_REQUEST,
            type=ErrorTypes.INJECTION_DETECTED.value,
            message=Messages.INJECTION_DETECTED.value,
        )
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=errorResponse.dict())

    # -------------------------------------------- Load database --------------------------------------------

    return TokenModel(sub=token_api_key_header)


async def checkTokenCapabilities(token: TokenModel, group: Groups) -> bool:
    """
    Checks whether the token is capable of accessing the endpoint.

    :param token: The token where will be tested.
    :return: True if the token is capable of accessing the endpoint else False.
    """
    # -------------------------------------------- Load database --------------------------------------------

    # Load data from the database into the client, if this didn't happen
    if not sqliteClient.wasLoaded:
        sqliteClient.startLoadTablesTask()
        await sqliteClient.loadTablesTask

    # -------------------------------------------- Check token exists --------------------------------------------

    # Load data from the database into the client, if this didn't happen
    if not sqliteClient.checkTokenExist(token=token.sub):
        errorResponse = Error(
            status=status.HTTP_401_UNAUTHORIZED,
            type=ErrorTypes.TOKEN_NOT_EXISTS.value,
            message=Messages.TOKEN_NOT_EXISTS.value,
        )
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=errorResponse.dict())

    # ---------------------------------------- Check token is privileged ----------------------------------------

    if not sqliteClient.checkTokenHasPrivileges(token=token.sub, group=group):
        errorResponse = Error(
            status=status.HTTP_401_UNAUTHORIZED,
            type=ErrorTypes.TOKEN_NOT_PRIVILEGED.value,
            message=Messages.TOKEN_NOT_PRIVILEGED.value,
        )
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=errorResponse.dict())

    return True
