"""
Recepcion de eventos de entrega de SendGrid.

Este endpoint es publico por necesidad: lo llama SendGrid, no un cliente
nuestro, asi que no puede exigir un token. La autenticacion es la firma
criptografica de la peticion, que se verifica antes de mirar el contenido.

Se responde lo antes posible y el procesamiento se delega a la cola: si la
respuesta tarda, SendGrid da la entrega por fallida y reenvia el lote entero,
generando duplicados y carga extra.
"""

from __future__ import annotations

import json
import logging

from django.conf import settings
from drf_spectacular.utils import extend_schema
from rest_framework import status
from rest_framework.permissions import AllowAny
from rest_framework.response import Response
from rest_framework.views import APIView

from core.utils.http import get_client_ip
from notifications.webhooks.sendgrid_signature import (
    SignatureVerificationError,
    verify_request,
)

logger = logging.getLogger("notifications.webhooks")

#: Tope defensivo. Los lotes de SendGrid rondan unos cientos de kilobytes.
MAX_WEBHOOK_BODY_BYTES = 5 * 1024 * 1024


class SendGridEventWebhook(APIView):
    """Recibe los eventos de entrega firmados por SendGrid."""

    authentication_classes: list = []
    permission_classes = [AllowAny]
    throttle_classes: list = []
    # Sin parsers: el cuerpo se lee crudo porque la firma se calcula sobre los
    # bytes exactos que llegaron. Deserializar y volver a serializar cambia el
    # orden de las claves y los espacios, y la firma dejaria de cuadrar.
    parser_classes: list = []

    @extend_schema(
        summary="Webhook de eventos de SendGrid",
        description=(
            "Endpoint que SendGrid usa para notificar entregas, rebotes, "
            "aperturas y clics. Requiere firma ECDSA valida."
        ),
        request=None,
        responses={204: None, 400: None, 401: None},
        auth=[],
    )
    def post(self, request, format=None):
        try:
            body = request.body
        except Exception:  # noqa: BLE001 - flujo ya consumido o mal formado
            logger.warning("No se pudo leer el cuerpo del webhook")
            return Response(status=status.HTTP_400_BAD_REQUEST)

        if len(body) > MAX_WEBHOOK_BODY_BYTES:
            logger.warning(
                "Cuerpo del webhook demasiado grande",
                extra={"size": len(body), "source_ip": get_client_ip(request)},
            )
            return Response(status=status.HTTP_413_REQUEST_ENTITY_TOO_LARGE)

        try:
            verify_request(request)
        except SignatureVerificationError as exc:
            logger.warning(
                "Webhook rechazado: %s",
                exc,
                extra={"source_ip": get_client_ip(request)},
            )
            # 401 sin detalle: quien manda la peticion o es SendGrid con la
            # clave correcta, o no tiene por que saber por que se rechazo.
            return Response(status=status.HTTP_401_UNAUTHORIZED)

        try:
            events = json.loads(body.decode("utf-8"))
        except (ValueError, UnicodeDecodeError):
            logger.warning("El cuerpo del webhook no es JSON valido")
            return Response(status=status.HTTP_400_BAD_REQUEST)

        if not isinstance(events, list):
            logger.warning("El webhook no envio una lista de eventos")
            return Response(status=status.HTTP_400_BAD_REQUEST)

        logger.info(
            "Lote de eventos recibido de SendGrid", extra={"events": len(events)}
        )

        from notifications.tasks import process_sendgrid_events_task

        request_id = getattr(request, "request_id", "")
        if settings.CELERY_TASK_ALWAYS_EAGER:
            process_sendgrid_events_task(events, request_id=request_id)
        else:
            process_sendgrid_events_task.delay(events, request_id=request_id)

        return Response(status=status.HTTP_204_NO_CONTENT)
