from enum import Enum
from http import HTTPStatus
from logging import Logger

from fastapi import HTTPException, Request
from fastapi.security.oauth2 import SecurityScopes
from pydantic import SecretStr

from phederation.security.authentication import UserInfo
from phederation.utils import configure_logger
from phederation.utils.settings import PhedSettings

from .authentication import Authentication


class PermissionType(str, Enum):
    NONE = "None"
    USER = "User"
    ADMIN = "Admin"


class Authorization:
    """Class to check the `PermissionType` for a given actor.
    Directly uses the OAuth2 Depends scheme, i.e. checks the token of the logged in user.
    """

    def __init__(self, settings: PhedSettings, authentication: Authentication):
        self.authentication: Authentication = authentication
        self.logger: Logger = configure_logger(__name__, prefix=settings.federation.logging_prefix)

    async def __call__(self, request: Request, security_scopes: SecurityScopes) -> UserInfo:
        """
        Authorization endpoint that requires a valid user info for access.
        Verifies that the logged in user has the requested security scopes as Realm Role in keycloak.
        """
        token = await self.authentication.oauth2_scheme_code(request)
        if not token:
            raise HTTPException(
                status_code=HTTPStatus.UNAUTHORIZED,
                detail="Not authenticated",
                headers={"WWW-Authenticate": "Bearer"},
            )
        token_secret = SecretStr(token)
        return await self.verify_token(token=token_secret, security_scopes=security_scopes)

    async def verify_token(self, token: SecretStr, security_scopes: SecurityScopes):
        self.logger.debug(f"Calling authorization endpoint, security_scopes={security_scopes.scopes}, token={token}")
        user_info: UserInfo | None = await self.authentication.verify_token(token)
        if not user_info:
            self.logger.debug(f"Could not get user info. Unauthorized")
            raise HTTPException(
                status_code=HTTPStatus.UNAUTHORIZED,
                detail="Permission level too low",
                headers={"WWW-Authenticate": "Bearer"},
            )

        # Verify that the user is allowed to access the requested scopes
        if not self.by_scope(user_info, security_scopes.scopes):
            raise HTTPException(
                status_code=HTTPStatus.UNAUTHORIZED,
                detail="Permission level too low",
                headers={"WWW-Authenticate": "Bearer"},
            )

        return user_info

    def by_scope(self, user_info: UserInfo, scopes: list[str]) -> bool:
        if not user_info.groups:
            self.logger.warning(f"UserInfo {user_info.preferred_username} does not include 'groups', cannot check authorization.")
            return False
        groups = [group.lower() for group in user_info.groups]
        for scope in scopes:
            if not user_info.groups or not scope in groups:
                self.logger.warning(f"User {user_info.preferred_username} tried to access permission level {scope}, not authorized.")
                raise HTTPException(
                    status_code=HTTPStatus.UNAUTHORIZED,
                    detail="User does not have required permission",
                    headers={"WWW-Authenticate": "Bearer"},
                )
        return True
