"""
Auditoria de notificaciones.

Sustituye al modelo `Notifications`, que solo guardaba `to`, `body`, `type` y
`status` y ademas obligaba a crear una fila en `Numbers` para cada
destinatario, lo que no encaja con el correo (una direccion no es un numero de
telefono) ni permite saber que paso con un envio concreto.

El modelo es comun a los tres canales desde el principio: el campo `channel`
distingue correo, SMS y WhatsApp. Asi, incorporar SMS y WhatsApp a esta
auditoria es cablear, no migrar el esquema otra vez.
"""

from __future__ import annotations

import uuid

from django.db import models
from django.utils import timezone


class Channel(models.TextChoices):
    EMAIL = "email", "Correo"
    SMS = "sms", "SMS"
    WHATSAPP = "whatsapp", "WhatsApp"


class MessageStatus(models.TextChoices):
    """Ciclo de vida de un envio."""

    # --- Estados propios de la API ---
    QUEUED = "queued", "En cola"
    SENDING = "sending", "Enviando"
    SENT = "sent", "Aceptado por el proveedor"
    FAILED = "failed", "Fallido"

    # --- Estados que llegan por webhook del proveedor ---
    DELIVERED = "delivered", "Entregado"
    DEFERRED = "deferred", "Diferido"
    BOUNCED = "bounced", "Rebotado"
    DROPPED = "dropped", "Descartado"
    SPAM = "spam", "Marcado como spam"
    READ = "read", "Leido"


#: Estados que ya no cambian por si solos. Los webhooks no garantizan orden de
#: entrega, asi que un evento que llegue tarde no debe degradar un estado que
#: ya es definitivo.
TERMINAL_STATUSES = frozenset(
    {
        MessageStatus.DELIVERED,
        MessageStatus.BOUNCED,
        MessageStatus.DROPPED,
        MessageStatus.SPAM,
    }
)


class NotificationMessage(models.Model):
    """Registro de una notificacion solicitada a la API."""

    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)

    channel = models.CharField(
        max_length=12, choices=Channel.choices, db_index=True
    )
    provider = models.CharField(max_length=20, db_index=True)
    status = models.CharField(
        max_length=20,
        choices=MessageStatus.choices,
        default=MessageStatus.QUEUED,
        db_index=True,
    )

    api_key = models.ForeignKey(
        "keys.ApiKeys",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="messages",
        verbose_name="Clave usada",
        help_text="Se conserva el registro aunque se borre la clave.",
    )

    # --- Destinatarios y cabeceras ---
    # `to`, `cc` y `bcc` son listas: direcciones de correo o numeros de
    # telefono segun el canal.
    to = models.JSONField(default=list)
    cc = models.JSONField(default=list, blank=True)
    bcc = models.JSONField(default=list, blank=True)
    recipient_count = models.PositiveIntegerField(default=0)

    subject = models.CharField(max_length=255, blank=True)
    sender = models.CharField(
        max_length=254,
        blank=True,
        help_text="Direccion o numero desde el que se envia.",
    )
    sender_name = models.CharField(max_length=128, blank=True)
    reply_to = models.CharField(max_length=254, blank=True)

    # --- Contenido (opcional, ver NOTIFICATIONS_STORE_BODY) ---
    template_id = models.CharField(max_length=128, blank=True)
    body_html = models.TextField(blank=True)
    body_text = models.TextField(blank=True)
    attachment_count = models.PositiveSmallIntegerField(default=0)
    attachment_bytes = models.PositiveIntegerField(default=0)

    # --- Resultado ---
    provider_message_id = models.CharField(
        max_length=255,
        blank=True,
        db_index=True,
        help_text="Identificador del proveedor; enlaza con los eventos del webhook.",
    )
    attempts = models.PositiveSmallIntegerField(default=0)
    error_code = models.CharField(max_length=64, blank=True)
    error_message = models.TextField(blank=True)

    # --- Trazabilidad ---
    request_id = models.CharField(max_length=64, blank=True, db_index=True)
    source_ip = models.GenericIPAddressField(null=True, blank=True)

    created_at = models.DateTimeField(auto_now_add=True, db_index=True)
    updated_at = models.DateTimeField(auto_now=True)
    sent_at = models.DateTimeField(null=True, blank=True)
    last_event_at = models.DateTimeField(null=True, blank=True)

    class Meta:
        db_table = "notification_message"
        verbose_name = "Notificación enviada"
        verbose_name_plural = "Notificaciones enviadas"
        ordering = ("-created_at",)
        indexes = [
            models.Index(fields=["channel", "-created_at"]),
            models.Index(fields=["status", "-created_at"]),
            models.Index(fields=["api_key", "-created_at"]),
        ]

    def __str__(self) -> str:
        return f"{self.channel} {self.id} [{self.status}]"

    @property
    def is_terminal(self) -> bool:
        return self.status in TERMINAL_STATUSES

    def mark_sending(self) -> None:
        self.status = MessageStatus.SENDING
        self.attempts += 1
        self.save(update_fields=["status", "attempts", "updated_at"])

    def mark_sent(self, provider_message_id: str = "") -> None:
        self.status = MessageStatus.SENT
        self.provider_message_id = provider_message_id or self.provider_message_id
        self.sent_at = timezone.now()
        self.error_code = ""
        self.error_message = ""
        self.save(
            update_fields=[
                "status", "provider_message_id", "sent_at",
                "error_code", "error_message", "updated_at",
            ]
        )

    def mark_failed(self, code: str = "", message: str = "") -> None:
        self.status = MessageStatus.FAILED
        self.error_code = (code or "")[:64]
        # El mensaje del proveedor puede ser muy largo y no aporta nada mas
        # alla de las primeras lineas.
        self.error_message = (message or "")[:2000]
        self.save(
            update_fields=["status", "error_code", "error_message", "updated_at"]
        )
