from typing import Optional, Tuple

from drf_spectacular.extensions import OpenApiAuthenticationExtension
from rest_framework import exceptions
from rest_framework.authentication import BaseAuthentication, get_authorization_header

from apps.auth_token.exceptions import InvalidToken
from apps.user_management.models import User

from .models import MobileAppAuthToken, MobileAppVerificationToken


class MobileAppVerificationTokenAuthentication(BaseAuthentication):
    model = MobileAppVerificationToken

    def authenticate(self, request) -> Tuple[User, MobileAppVerificationToken]:
        auth = get_authorization_header(request).decode("utf-8")
        user, auth_token = self.authenticate_credentials(auth)
        return user, auth_token

    def authenticate_credentials(self, token_string: str) -> Tuple[User, MobileAppVerificationToken]:
        try:
            auth_token = self.model.validate_token_string(token_string)
        except InvalidToken:
            raise exceptions.AuthenticationFailed("Invalid token")

        return auth_token.user, auth_token


class MobileAppAuthTokenAuthentication(BaseAuthentication):
    model = MobileAppAuthToken

    def authenticate(self, request) -> Optional[Tuple[User, MobileAppAuthToken]]:
        auth = get_authorization_header(request).decode("utf-8")
        user, auth_token = self.authenticate_credentials(auth)
        if user is None or not user.is_active:
            return None
        return user, auth_token

    def authenticate_credentials(self, token_string: str) -> Tuple[Optional[User], Optional[MobileAppAuthToken]]:
        try:
            auth_token = self.model.validate_token_string(token_string)
        except InvalidToken:
            return None, None

        return auth_token.user, auth_token


class MobileAppAuthTokenAuthenticationSchema(OpenApiAuthenticationExtension):
    target_class = MobileAppAuthTokenAuthentication
    name = "MobileAppAuthTokenAuthentication"

    def get_security_definition(self, auto_schema):
        return {
            "type": "apiKey",
            "in": "header",
            "name": "Authorization",
        }
