"""
Canje de una clave de API por un token de acceso.

Emitia el token con `Token.objects.get_or_create(user=request.user)`, lo que
tenia dos consecuencias: el token era el mismo para todas las claves de una
misma cuenta —revocar una no invalidaba las sesiones abiertas con ella— y no
guardaba ninguna relacion con la credencial presentada, asi que la
autorizacion no tenia nada sobre lo que decidir.

Ahora el token se emite a nombre de la clave concreta que lo pidio.
"""

from __future__ import annotations

from drf_spectacular.utils import extend_schema
from rest_framework import status
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView

from core.authentications.api_key_auth import ApiKeyAuthentication
from keys.models.access_token import AccessToken
from notifications.throttling import TokenIssueThrottle


class Token(APIView):
    authentication_classes = [ApiKeyAuthentication]
    # Sin esto, una peticion sin X-API-Key quedaba anonima y aun asi entraba
    # en la vista, que hacia get_or_create sobre `request.user` (anonimo).
    permission_classes = [IsAuthenticated]
    throttle_classes = [TokenIssueThrottle]

    def initial(self, request, *args, **kwargs):
        """
        Cuenta el intento ANTES de autenticar.

        `TokenIssueThrottle` existia pero no la usaba ninguna vista, y
        declararla sin mas tampoco habria bastado: DRF autentica en
        `perform_authentication()` y solo despues llama a `check_throttles()`,
        asi que `AuthenticationFailed` corta la peticion antes de que el
        intento fallido llegue a contarse. Con el limite detras, se podian
        probar claves a la velocidad que se quisiera: todas las respuestas
        eran 401 y ninguna 429.
        """
        self.check_throttles(request)
        super().initial(request, *args, **kwargs)

    @extend_schema(
        summary="Canjear una clave de API por un token de acceso",
        description=(
            "El token es de un solo uso y caduca. `scopes` indica que "
            "operaciones autoriza la clave que lo pidio."
        ),
        responses={200: None, 401: None},
    )
    def get(self, request, format=None):
        clave = request.api_key_record
        registro, token = AccessToken.issue(clave)

        return Response(
            {
                # `status` y `token` se conservan por compatibilidad: es lo
                # unico que leen los clientes actuales.
                "status": True,
                "status_code": status.HTTP_200_OK,
                "token": token,
                "expires_at": registro.expires_at,
                "scopes": clave.scopes or [],
            },
            status=status.HTTP_200_OK,
        )
