from __future__ import annotations

from typing import Any, Mapping

from ..config import CognitoAuthConfig
from ..exceptions import AuthenticationError, AuthorizationError, TokenError
from ..models import AuthResults
from ..permissions import role_has_permission
from ..services.role_mapper import resolve_role_from_claims
from ..services.token_verifier import TokenVerifier
from ..utils import normalize_bearer_token


class AuthService:
    def __init__(
        self,
        config: CognitoAuthConfig,
        token_verifier: TokenVerifier | None = None,
    ) -> None:
        self._config = config
        self._token_verifier = token_verifier if token_verifier is not None else TokenVerifier(config)

    def authenticate(self, token: str | None) -> AuthResults:
        normalized_token = normalize_bearer_token(token)
        if not normalized_token:
            return AuthResults(ok=False, error=AuthenticationError('Invalid token'))

        try:
            claims = self._token_verifier.verify(normalized_token)
        except TokenError as e:
            return AuthResults(ok=False, error=e)

        role = resolve_role_from_claims(claims, self._config)

        return AuthResults(ok=True, claims=claims, role=role)

    def get_role(self, token: str | None) -> str | None:
        results = self.authenticate(token)
        if not results.ok:
            return None 
        return results.role

    def has_permission(self, token: str | None, permission: str) -> bool:
        result = self.authenticate(token)
        if not result.ok:
            return False
        return role_has_permission(result.role, permission)

    def require_permission(self, token: str | None, permission: str) -> Mapping[str, Any]:
        normalized_token = normalize_bearer_token(token)
        if not normalized_token:
            raise AuthenticationError('Invalid token')

        try:
            claims = self._token_verifier.verify(normalized_token)
        except TokenError as e:
            raise AuthenticationError(f'Authentication failed: {e}') from e
            
        role = resolve_role_from_claims(claims, self._config)
        if not role:
            raise AuthenticationError('No role found')

        if not role_has_permission(role, permission):
            raise AuthorizationError(f'Permission {permission} not allowed for role {role}')

        return claims

    