"""
Tareas Celery — App Notifications
=====================================
Procesamiento asíncrono de mensajes WhatsApp.
Nunca bloquean el request del usuario.

Tareas:
1. enviar_whatsapp — Envío individual con reintentos exponenciales
2. notificar_cita_confirmada — Cita confirmada → mensaje al paciente
3. notificar_cita_cancelada — Cita cancelada → mensaje al paciente
4. notificar_factura_disponible — Factura creada → mensaje al paciente
5. recordatorio_citas_24h — Tarea periódica Celery Beat (diaria)
"""

import logging

from celery import shared_task
from django.conf import settings
from django.utils import timezone

logger = logging.getLogger("apps.notifications")


# ══════════════════════════════════════════════════════════════
# TAREA BASE: Envío con reintentos exponenciales
# ══════════════════════════════════════════════════════════════
@shared_task(
    bind=True,
    max_retries=3,
    acks_late=True,
    reject_on_worker_lost=True,
)
def enviar_whatsapp(self, log_id):
    """
    Envía un mensaje WhatsApp y actualiza el log.
    Reintentos: 1min → 5min → 15min (backoff exponencial).
    """
    from .models import WhatsAppLog
    from .services import build_body_params, whatsapp_client

    try:
        log = WhatsAppLog.objects.get(id=log_id, is_active=True)
    except WhatsAppLog.DoesNotExist:
        logger.error("WhatsAppLog %s no encontrado", log_id)
        return

    # Incrementar intentos
    log.intentos += 1
    log.estado = WhatsAppLog.Estado.REINTENTANDO
    log.save(update_fields=["intentos", "estado", "updated_at"])

    # Construir componentes si hay params
    components = None
    if log.mensaje_params:
        params = log.mensaje_params.get("body", [])
        if params:
            components = build_body_params(*params)

    # Enviar
    result = whatsapp_client.enviar_template(
        numero=log.numero_telefono,
        template_name=log.mensaje_template,
        language="pt",
        components=components,
    )

    if result["ok"]:
        log.estado = WhatsAppLog.Estado.ENVIADO
        log.whatsapp_message_id = result["message_id"]
        log.error_detalle = ""
        log.save(update_fields=[
            "estado", "whatsapp_message_id", "error_detalle", "updated_at",
        ])
        logger.info("WhatsApp log %s enviado OK", log_id)
        return

    # Error — decidir si reintentar
    log.error_detalle = result["error"]

    retry_delays = getattr(settings, "WHATSAPP_RETRY_DELAYS", [60, 300, 900])
    max_retries = getattr(settings, "WHATSAPP_MAX_RETRIES", 3)

    if log.intentos < max_retries:
        delay = retry_delays[min(log.intentos - 1, len(retry_delays) - 1)]
        log.estado = WhatsAppLog.Estado.REINTENTANDO
        log.save(update_fields=["estado", "error_detalle", "updated_at"])
        logger.warning(
            "WhatsApp log %s falló (intento %d/%d), reintentando en %ds: %s",
            log_id, log.intentos, max_retries, delay, result["error"],
        )
        raise self.retry(countdown=delay)
    else:
        log.estado = WhatsAppLog.Estado.DESCARTADO
        log.save(update_fields=["estado", "error_detalle", "updated_at"])
        logger.error(
            "WhatsApp log %s descartado tras %d intentos: %s",
            log_id, log.intentos, result["error"],
        )


# ══════════════════════════════════════════════════════════════
# HELPERS: Crear log y encolar envío
# ══════════════════════════════════════════════════════════════
def _crear_y_encolar(destinatario, numero, evento, template_name, params, cita=None, factura=None):
    """
    Crea un WhatsAppLog y encola la tarea de envío.
    No lanza excepciones — fallo de WA no interrumpe el flujo.
    """
    from .models import WhatsAppLog

    if not numero:
        logger.warning(
            "WhatsApp no enviado: paciente %s sin teléfono (evento=%s)",
            destinatario.email if destinatario else "?",
            evento,
        )
        return None

    try:
        log = WhatsAppLog.objects.create(
            destinatario=destinatario,
            numero_telefono=numero,
            evento=evento,
            mensaje_template=template_name,
            mensaje_params={"body": params} if params else {},
            cita=cita,
            factura=factura,
        )
        enviar_whatsapp.delay(str(log.id))
        logger.info(
            "WhatsApp encolado: evento=%s, destino=***%s, log=%s",
            evento, str(numero)[-4:], log.id,
        )
        return log
    except Exception:
        logger.exception("Error creando WhatsAppLog para evento=%s", evento)
        return None


# ══════════════════════════════════════════════════════════════
# EVENTO 1: Cita Confirmada
# ══════════════════════════════════════════════════════════════
@shared_task
def notificar_cita_confirmada(cita_id):
    """Envía notificación WhatsApp al paciente cuando la cita es confirmada."""
    from apps.appointments.models import Cita
    from .models import WhatsAppLog

    try:
        cita = Cita.objects.select_related("paciente", "servicio").get(
            id=cita_id, is_active=True,
        )
    except Cita.DoesNotExist:
        logger.error("notificar_cita_confirmada: Cita %s no encontrada", cita_id)
        return

    paciente = cita.paciente
    params = [
        paciente.nombre,
        cita.servicio.nombre,
        cita.fecha_hora.strftime("%d/%m/%Y"),
        cita.fecha_hora.strftime("%H:%M"),
    ]

    _crear_y_encolar(
        destinatario=paciente,
        numero=paciente.telefono,
        evento=WhatsAppLog.Evento.CITA_CONFIRMADA,
        template_name="cita_confirmada",
        params=params,
        cita=cita,
    )


# ══════════════════════════════════════════════════════════════
# EVENTO 2: Recordatorio 24h
# ══════════════════════════════════════════════════════════════
@shared_task
def recordatorio_citas_24h():
    """
    Tarea periódica (Celery Beat): busca citas confirmadas en las próximas 24h
    y envía recordatorio por WhatsApp.
    """
    from apps.appointments.models import Cita
    from .models import WhatsAppLog

    ahora = timezone.now()
    ventana_inicio = ahora + timezone.timedelta(hours=23)
    ventana_fin = ahora + timezone.timedelta(hours=25)

    citas = Cita.objects.filter(
        estado=Cita.Estado.CONFIRMADA,
        fecha_hora__gte=ventana_inicio,
        fecha_hora__lte=ventana_fin,
        is_active=True,
    ).select_related("paciente", "servicio")

    enviados = 0
    for cita in citas:
        # Evitar duplicados: no enviar si ya hay un recordatorio para esta cita
        ya_enviado = WhatsAppLog.objects.filter(
            cita=cita,
            evento=WhatsAppLog.Evento.RECORDATORIO_24H,
            estado__in=[WhatsAppLog.Estado.ENVIADO, WhatsAppLog.Estado.REINTENTANDO],
            is_active=True,
        ).exists()

        if ya_enviado:
            continue

        paciente = cita.paciente
        params = [
            paciente.nombre,
            cita.servicio.nombre,
            cita.fecha_hora.strftime("%d/%m/%Y"),
            cita.fecha_hora.strftime("%H:%M"),
        ]

        log = _crear_y_encolar(
            destinatario=paciente,
            numero=paciente.telefono,
            evento=WhatsAppLog.Evento.RECORDATORIO_24H,
            template_name="recordatorio_cita_24h",
            params=params,
            cita=cita,
        )
        if log:
            enviados += 1

    logger.info(
        "Recordatorio 24h: %d citas encontradas, %d recordatorios encolados",
        citas.count(),
        enviados,
    )
    return enviados


# ══════════════════════════════════════════════════════════════
# EVENTO 3: Cita Cancelada
# ══════════════════════════════════════════════════════════════
@shared_task
def notificar_cita_cancelada(cita_id, motivo=""):
    """Envía notificación WhatsApp al paciente cuando la cita es cancelada."""
    from apps.appointments.models import Cita
    from .models import WhatsAppLog

    try:
        cita = Cita.objects.select_related("paciente", "servicio").get(
            id=cita_id, is_active=True,
        )
    except Cita.DoesNotExist:
        logger.error("notificar_cita_cancelada: Cita %s no encontrada", cita_id)
        return

    paciente = cita.paciente
    params = [
        paciente.nombre,
        cita.servicio.nombre,
        cita.fecha_hora.strftime("%d/%m/%Y"),
        cita.fecha_hora.strftime("%H:%M"),
        motivo or "Motivo no especificado",
    ]

    _crear_y_encolar(
        destinatario=paciente,
        numero=paciente.telefono,
        evento=WhatsAppLog.Evento.CITA_CANCELADA,
        template_name="cita_cancelada",
        params=params,
        cita=cita,
    )


# ══════════════════════════════════════════════════════════════
# EVENTO 4: Factura Disponible
# ══════════════════════════════════════════════════════════════
@shared_task
def notificar_factura_disponible(factura_id):
    """Envía notificación WhatsApp al paciente cuando una factura está disponible."""
    from apps.finance.models import Invoice
    from .models import WhatsAppLog

    try:
        factura = Invoice.objects.select_related("paciente", "servicio").get(
            id=factura_id, is_active=True,
        )
    except Invoice.DoesNotExist:
        logger.error("notificar_factura_disponible: Factura %s no encontrada", factura_id)
        return

    paciente = factura.paciente
    site_url = getattr(settings, "SITE_URL", "http://localhost:8000")
    portal_url = f"{site_url}/cuenta/panel/"

    params = [
        paciente.nombre,
        factura.numero,
        f"{factura.total:,.2f} Kz".replace(",", "X").replace(".", ",").replace("X", "."),
        portal_url,
    ]

    _crear_y_encolar(
        destinatario=paciente,
        numero=paciente.telefono,
        evento=WhatsAppLog.Evento.FACTURA_DISPONIBLE,
        template_name="factura_disponible",
        params=params,
        factura=factura,
    )
