"""
Recepcion de los avisos de estado de Twilio.

Twilio llama a esta URL cada vez que un SMS o un mensaje de WhatsApp cambia de
estado: encolado, enviado, entregado, leido, rechazado. Sin esto, la auditoria
solo sabe que el proveedor acepto el mensaje, que no es lo mismo que haber
llegado al telefono.

La URL lleva el identificador del envio para poder correlacionar el aviso sin
depender solo del SID de Twilio.
"""

from __future__ import annotations

import logging

from django.conf import settings
from drf_spectacular.utils import extend_schema
from rest_framework import status
from rest_framework.parsers import FormParser, MultiPartParser
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.twilio_signature import (
    SignatureVerificationError,
    verify_request,
)

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


class TwilioStatusWebhook(APIView):
    """Recibe los cambios de estado firmados por Twilio."""

    authentication_classes: list = []
    permission_classes = [AllowAny]
    throttle_classes: list = []
    # Twilio manda `application/x-www-form-urlencoded`.
    parser_classes = [FormParser, MultiPartParser]

    @extend_schema(
        summary="Webhook de estado de Twilio",
        description=(
            "Endpoint que Twilio usa para notificar el estado de cada SMS o "
            "mensaje de WhatsApp. Requiere firma X-Twilio-Signature valida."
        ),
        request=None,
        responses={204: None, 401: None},
        auth=[],
    )
    def post(self, request, message_id, format=None):
        try:
            verify_request(request._request)
        except SignatureVerificationError as exc:
            logger.warning(
                "Callback de Twilio rechazado: %s",
                exc,
                extra={"source_ip": get_client_ip(request), "message_id": str(message_id)},
            )
            # 401 sin detalle: quien llama o es Twilio con el token correcto,
            # o no tiene por que saber por que se rechazo.
            return Response(status=status.HTTP_401_UNAUTHORIZED)

        # Se procesa EXACTAMENTE la misma fuente que se acaba de verificar.
        #
        # `request.data` no vale: DRF fusiona `request.FILES` encima de `POST`
        # (Request._full_data), y Django manda a FILES toda parte multipart que
        # lleve `filename`. Esas partes no entran en el calculo de la firma de
        # Twilio, pero ganan al fusionarse porque `.dict()` se queda con el
        # ultimo valor. Con una firma capturada, un tercero podia reenviarla
        # anadiendo una parte `name="MessageStatus"; filename="failed"` y
        # cambiar el estado que la firma decia proteger.
        payload = request._request.POST.dict()

        logger.info(
            "Aviso de estado recibido de Twilio",
            extra={
                "message_id": str(message_id),
                "twilio_status": payload.get("MessageStatus"),
            },
        )

        from notifications.tasks import process_twilio_status_task

        request_id = getattr(request, "request_id", "")
        if settings.CELERY_TASK_ALWAYS_EAGER:
            process_twilio_status_task(str(message_id), payload, request_id=request_id)
        else:
            process_twilio_status_task.delay(
                str(message_id), payload, request_id=request_id
            )

        return Response(status=status.HTTP_204_NO_CONTENT)
