"""RIMS projects domain signals + M&EL feed wiring.

Custom dispatch signals are kept for downstream consumers that prefer the
semantic event over a raw post_save subscription. The model-level
post_save receivers below feed the M&EL DataPoint pipeline whenever a
Milestone transitions to ACCEPTED or OVERDUE, satisfying the PRD's
"M&E and Feedback System" requirement that project milestones surface as
indicator data points.
"""
from __future__ import annotations

import logging

import django.dispatch
from django.db.models.signals import post_save, pre_save
from django.dispatch import receiver

from apps.rims.projects.models import Milestone

logger = logging.getLogger(__name__)

project_milestone_updated = django.dispatch.Signal()
project_risk_flagged = django.dispatch.Signal()


@receiver(pre_save, sender=Milestone)
def _milestone_cache_previous_status(sender, instance, **kwargs):
    if not instance.pk:
        instance._rims_prev_milestone_status = None
        return
    try:
        old = Milestone.objects.only("status").get(pk=instance.pk)
        instance._rims_prev_milestone_status = old.status
    except Milestone.DoesNotExist:
        instance._rims_prev_milestone_status = None


@receiver(post_save, sender=Milestone)
def _milestone_emit_mel_event(sender, instance, created, **kwargs):
    """PRD §4.3.4 — feed milestone transitions into the M&EL DataPoint
    pipeline. Failures inside the M&EL feed must not roll back the
    milestone update."""
    prev = getattr(instance, "_rims_prev_milestone_status", None)
    if instance.status not in {Milestone.Status.ACCEPTED, Milestone.Status.OVERDUE}:
        return
    if prev == instance.status:
        return
    try:
        from apps.mel.tracking import services as mel_services

        if instance.status == Milestone.Status.ACCEPTED:
            mel_services.record_milestone_accepted(instance)
        else:
            mel_services.record_milestone_overdue(instance)
    except Exception:  # pragma: no cover - feed failures must not roll back
        logger.exception("mel.record_milestone_* failed milestone_id=%s", instance.pk)
