"""Explicit post-commit integration points; no frozen workflow is modified."""
from datetime import datetime
import logging

from django.core.exceptions import ValidationError
from django.db import connection, transaction
from django.utils import timezone

from apps.access.authorization import require_permission, authorized_queryset
from apps.frontdesk.models import Appointment
from apps.organization.models import ServiceCenter
from apps.service.models import ServiceCase
from apps.commercial.models import ServiceQuotation, ServicePayment
from .services import request_notification

EVENTS = ("APPOINTMENT_CONFIRMATION", "APPOINTMENT_REMINDER", "SERVICE_INTAKE", "QUOTATION_READY",
          "QUOTATION_APPROVED", "READY_FOR_DELIVERY", "PAYMENT_RECEIPT")


def workflow_notification(*, actor, template, event, record, policy=""):
    if connection.in_atomic_block:
        raise ValidationError("Invoke hooks after commit, or use after_commit_notification.")
    if event not in EVENTS:
        raise ValidationError("Unsupported business event.")
    context = {}
    if event.startswith("APPOINTMENT_"):
        row = Appointment.objects.select_related("slot", "customer", "service_center", "device__product_model").get(pk=record.pk)
        if row.status != "SCHEDULED":
            raise ValidationError("Appointment is no longer scheduled.")
        if event == "APPOINTMENT_REMINDER":
            import re
            if not re.fullmatch(r"[a-zA-Z0-9_-]{1,64}", policy):
                raise ValidationError("An explicit reminder policy identifier is required.")
            start = timezone.make_aware(datetime.combine(row.slot.date, row.slot.start_time), timezone.get_default_timezone())
            if start <= timezone.now():
                raise ValidationError("Reminder requires an upcoming appointment.")
        customer, center = row.customer, row.service_center
        require_permission(user=actor, permission="frontdesk.view_appointment", target=center)
        context.update(appointment_date=row.slot.date.isoformat(), appointment_time=row.slot.start_time.strftime("%H:%M"))
        reference = str(row.pk)
        device = row.device
    else:
        if event.startswith("QUOTATION_"):
            row = ServiceQuotation.objects.select_related("family__service_case").get(pk=record.pk)
            if not row.is_current or row.status != ("SUBMITTED" if event == "QUOTATION_READY" else "APPROVED"):
                raise ValidationError("Quotation is not in the requested authoritative state.")
            case = row.family.service_case
            require_permission(user=actor, permission="commercial.view_servicequotation", target=case.service_center)
            reference = f"{row.family.number} / revision {row.revision}"
            context["quotation_reference"] = reference
        elif event == "PAYMENT_RECEIPT":
            row = ServicePayment.objects.select_related("invoice__service_case", "receipt").get(pk=record.pk)
            if row.status != "POSTED" or not hasattr(row, "receipt"):
                raise ValidationError("A posted payment and issued receipt are required.")
            case = row.invoice.service_case
            require_permission(user=actor, permission="commercial.view_servicepayment", target=case.service_center)
            reference = row.receipt.number
            context.update(receipt_reference=reference, payment_amount=f"{row.currency} {row.amount}", invoice_reference=row.invoice.number)
            from apps.commercial.payment_queries import invoice_settlement_summary
            balance = invoice_settlement_summary(actor=actor, invoice=row.invoice)
            context["amount_due"] = f"{balance['currency']} {balance['balance']}"
        else:
            row = case = ServiceCase.objects.get(pk=record.pk)
            if event == "READY_FOR_DELIVERY" and case.status != "READY_FOR_DELIVERY":
                raise ValidationError("Case is not ready for delivery.")
            require_permission(user=actor, permission="service.view_servicecase", target=case.service_center)
            reference = case.job_number
        customer, center, device = case.customer, case.service_center, case.device
        context["service_case_reference"] = case.job_number
    if device:
        context["device_name"] = str(device.product_model)
    return request_notification(actor=actor, template=template, customer=customer, service_center=center,
        event=event, related_id=row.pk, related_reference=reference,
        event_key=f"{event}:{row.pk}:{policy if event == 'APPOINTMENT_REMINDER' else ''}", context=context)


def after_commit_notification(**kwargs):
    """Optional caller hook: rollback discards it; preparation errors cannot fail commit."""
    def request():
        try:
            workflow_notification(**kwargs)
        except Exception:
            # No exception repr/traceback: providers/configuration may carry secrets.
            from uuid import UUID
            event = kwargs.get("event")
            event = event if event in EVENTS else "INVALID_EVENT"
            try:
                reference = str(UUID(str(kwargs["record"].pk)))
            except Exception:
                reference = "unavailable"
            logging.getLogger("communications").error(
                "NOTIFICATION_PREPARATION_FAILED event=%s reference=%s: review and retry after commit", event, reference)
    transaction.on_commit(request)


def event_is_current(notification):
    """Suppress obsolete queued operational notices without rewriting snapshots."""
    event, pk = notification.event, notification.related_id
    if event.startswith("APPOINTMENT_"):
        row = Appointment.objects.select_related("slot").filter(pk=pk, status="SCHEDULED").first()
        if row is None:
            return False
        if event == "APPOINTMENT_REMINDER":
            return timezone.make_aware(datetime.combine(row.slot.date, row.slot.start_time), timezone.get_default_timezone()) > timezone.now()
    elif event.startswith("QUOTATION_"):
        return ServiceQuotation.objects.filter(pk=pk, is_current=True,
            status="SUBMITTED" if event == "QUOTATION_READY" else "APPROVED").exists()
    elif event == "READY_FOR_DELIVERY":
        return ServiceCase.objects.filter(pk=pk, status="READY_FOR_DELIVERY").exists()
    elif event == "PAYMENT_RECEIPT":
        return ServicePayment.objects.filter(pk=pk, status="POSTED").exists()
    return True


def queue_appointment_reminders(*, actor, template, date, policy):
    centers = authorized_queryset(user=actor, permission="communications.send_notification", queryset=ServiceCenter.objects.all())
    centers = authorized_queryset(user=actor, permission="frontdesk.view_appointment", queryset=centers)
    rows = Appointment.objects.filter(company_id=template.company_id, service_center__in=centers,
                                     status="SCHEDULED", slot__date=date).select_related("slot")
    notifications = []
    for row in rows:
        start = timezone.make_aware(datetime.combine(row.slot.date, row.slot.start_time), timezone.get_default_timezone())
        if start > timezone.now():
            notifications.append(workflow_notification(actor=actor, template=template, event="APPOINTMENT_REMINDER", record=row, policy=policy))
    return notifications
