"""PRD §5.4 FRFM046 — synchronous GL posting on DisbursementExecution save.

User lock 3 (both sync + Celery): the post_save signal writes GLEntry rows
inline when the execution flips to EXECUTED, but the call is wrapped in
try/except so a posting failure never propagates a 500 into the request
cycle. When the inline write fails, the execution stays at
``gl_posting_status="pending"`` and the daily Celery beat
(``rims-finance-gl-reconcile``) posts the missing entries during the next
sweep.
"""
from __future__ import annotations

import logging

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

logger = logging.getLogger(__name__)


@receiver(post_save, sender="rims_finance.DisbursementExecution")
def post_gl_entries_on_save(sender, instance, created, update_fields=None, **kwargs):
    from apps.rims.finance.models import DisbursementExecution
    from apps.rims.finance.services_gl import post_gl_entries

    if instance.status != DisbursementExecution.Status.EXECUTED:
        return
    if instance.gl_posting_status == "posted":
        return
    try:
        post_gl_entries(instance)
        DisbursementExecution.objects.filter(pk=instance.pk).update(
            gl_posting_status="posted"
        )
    except Exception:  # pragma: no cover
        logger.exception(
            "GL posting failed for execution %s; deferring to nightly beat.",
            instance.pk,
        )
        DisbursementExecution.objects.filter(pk=instance.pk).update(
            gl_posting_status="pending"
        )
