"""Finance workflow email / in-app notifications."""

from __future__ import annotations

from django.contrib.auth import get_user_model
from django.db.models import Q

from apps.core.notifications.emailing import rims_email_context
from apps.core.notifications.models import Notification
from apps.core.notifications.services import bulk_notify, send_notification
from apps.core.permissions.roles import UserRole
from django.urls import reverse

from apps.rims.finance.models import DisbursementRequest

User = get_user_model()


def _finance_alerts_users():
    # FINANCE_MANAGER added per P0 RBAC sweep — distinct approval tier in
    # addition to FINANCE_OFFICER so a submitted disbursement reaches both
    # the requesting officer pool and the approver pool.
    qs = User.objects.filter(is_active=True).filter(
        Q(
            role__in=[
                UserRole.ADMIN,
                UserRole.GRANTS_MANAGER,
                UserRole.FINANCE_OFFICER,
                UserRole.FINANCE_MANAGER,
            ]
        )
    )
    return qs.distinct()


def notify_disbursement_submitted(req: DisbursementRequest) -> None:
    award = req.budget.award
    app = award.application
    msg = (
        f"Disbursement request submitted: {req.amount} for budget “{req.budget.name}” "
        f"(award #{award.pk}, {app.applicant.email})."
    )
    ctx = rims_email_context(
        subject="Disbursement request needs review",
        headline="New disbursement request",
        action_path=reverse("rims_finance:disbursement_detail", kwargs={"pk": req.pk}),
        action_label="Review request",
        preheader="A disbursement request was submitted for approval.",
    )
    bulk_notify(
        list(_finance_alerts_users()),
        msg,
        verb=Notification.Verb.FINANCE_DISBURSEMENT,
        email_context=ctx,
        action_url=reverse("rims_finance:disbursement_detail", kwargs={"pk": req.pk}),
    )


def notify_payment_executed_payee(req: DisbursementRequest, *, execution=None) -> None:
    """PRD §5.4 FRFM046 — payment-completion notification.

    When DisbursementRequest.payee_email is set, the message goes to the
    external payee (not a system user, so we use ``send_notification`` with
    a synthetic recipient — but our notification model requires a User row).
    Practical compromise: when payee_email is set we ALSO email the requester
    so they have a record and can confirm with the payee out-of-band; when
    it's blank we email the requester only.
    """
    recipient = req.requested_by
    if not recipient:
        return
    body = (
        f"Disbursement of {req.amount} {req.budget.currency} on budget "
        f'"{req.budget.name}" was executed.'
    )
    if req.payee_email:
        body += f" Payee notification was sent to {req.payee_email}."
    if execution and getattr(execution, "execution_reference", ""):
        body += f"\nReference: {execution.execution_reference}"
    ctx = rims_email_context(
        subject="Payment executed",
        headline="Payment executed",
        action_path=reverse("rims_finance:disbursement_detail", kwargs={"pk": req.pk}),
        action_label="View disbursement",
        preheader="A disbursement has been executed.",
    )
    send_notification(
        recipient,
        body,
        verb=Notification.Verb.FINANCE_DISBURSEMENT,
        email_context=ctx,
        action_url=reverse("rims_finance:disbursement_detail", kwargs={"pk": req.pk}),
    )


def notify_payment_due(schedule) -> None:
    """PRD §5.4 FRFM026 — alert when a PaymentSchedule row is due soon. Sends
    to the budget award's finance officer if known, otherwise the
    finance-alerts user pool."""
    award = schedule.budget.award
    funding_record = getattr(award.application.call, "funding_record", None)
    finance_officer = getattr(funding_record, "finance_officer", None) if funding_record else None
    msg = (
        f"Payment due {schedule.due_on}: {schedule.amount} {schedule.budget.currency} "
        f'on budget "{schedule.budget.name}" ({schedule.label or "unlabelled"}).'
    )
    ctx = rims_email_context(
        subject=f"Payment due {schedule.due_on}",
        headline="Upcoming payment",
        action_path=reverse("rims_finance:budget_detail", kwargs={"pk": schedule.budget.pk}),
        action_label="Open budget",
        preheader="A scheduled payment is approaching its due date.",
    )
    if finance_officer:
        send_notification(
            finance_officer,
            msg,
            verb=Notification.Verb.FINANCE_DISBURSEMENT,
            email_context=ctx,
        )
        return
    bulk_notify(
        list(_finance_alerts_users()),
        msg,
        verb=Notification.Verb.FINANCE_DISBURSEMENT,
        email_context=ctx,
    )


def notify_disbursement_decision(req: DisbursementRequest, *, approved: bool) -> None:
    recipient = req.requested_by
    if not recipient:
        return
    if approved:
        subj = "Disbursement request approved"
        headline = "Request approved"
        body = (
            f"Your disbursement request for {req.amount} on budget “{req.budget.name}” "
            "has been approved."
        )
    else:
        subj = "Disbursement request rejected"
        headline = "Request rejected"
        body = (
            f"Your disbursement request for {req.amount} on budget “{req.budget.name}” "
            "was rejected. Open the request for details or contact finance staff."
        )
    ctx = rims_email_context(
        subject=subj,
        headline=headline,
        action_path=reverse("rims_finance:disbursement_detail", kwargs={"pk": req.pk}),
        action_label="View disbursement",
        preheader=headline,
    )
    send_notification(
        recipient,
        body,
        verb=Notification.Verb.FINANCE_DISBURSEMENT,
        email_context=ctx,
        action_url=reverse("rims_finance:disbursement_detail", kwargs={"pk": req.pk}),
    )


def notify_payment_failure(execution) -> None:
    """PRD §5.4 FRFM042/043 — alert finance staff when an execution flips to
    FAILED. Targets the budget's funding-record finance officer when known,
    otherwise the requester."""
    req = execution.request
    award = req.budget.award
    funding_record = getattr(getattr(award, "application", None) and award.application.call, "funding_record", None)
    finance_officer = getattr(funding_record, "finance_officer", None) if funding_record else None
    recipient = finance_officer or req.requested_by
    if not recipient:
        return
    cat = execution.get_failure_category_display() if execution.failure_category else "Other"
    body = (
        f"Payment of {req.amount} {req.budget.currency} on budget "
        f'"{req.budget.name}" failed ({cat}). '
        f"Reason: {execution.failure_reason or '—'}"
    )
    ctx = rims_email_context(
        subject="Payment failed — needs retry",
        headline="Payment failed",
        action_path=reverse("rims_finance:disbursement_detail", kwargs={"pk": req.pk}),
        action_label="Open disbursement",
        preheader="A scheduled payment failed. Open the disbursement to manage the retry queue.",
    )
    send_notification(
        recipient,
        body,
        verb=Notification.Verb.FINANCE_DISBURSEMENT,
        email_context=ctx,
        action_url=reverse("rims_finance:disbursement_detail", kwargs={"pk": req.pk}),
    )
