"""PRD §5.3 — scholarship operations services.

Service-layer wrappers around the scholarship UI mutations so business
rules (audit logging, validation) live in one place rather than duplicated
across views.
"""

from __future__ import annotations

from decimal import Decimal

from django.core.exceptions import ValidationError
from django.db import IntegrityError, transaction

from apps.core.audit.mixins import log_audit
from apps.rims.scholarships.models import GraduationRecord, ProgressReport, Scholar, Stipend


@transaction.atomic
def submit_progress_report(
    scholar: Scholar,
    *,
    period_label: str,
    file=None,
    notes: str = "",
    actor=None,
    extra_fields: dict | None = None,
    supervisor_formset=None,
    skills_formset=None,
    manuscript_formset=None,
    conference_formset=None,
    presentation_formset=None,
) -> ProgressReport:
    """Create or update a ProgressReport for *scholar*.

    Accepts structured content fields via extra_fields dict and optional
    inline formsets for sub-models (supervisors, skills, manuscripts, etc.).
    Enforces uniqueness on (scholar, period_label).
    """
    from django.utils import timezone

    period_label = (period_label or "").strip()
    if not period_label:
        raise ValidationError({"period_label": "Period label is required."})
    if ProgressReport.objects.filter(scholar=scholar, period_label=period_label).exists():
        raise ValidationError(
            {"period_label": f"A progress report for {period_label!r} already exists."}
        )

    create_kwargs: dict = dict(
        scholar=scholar,
        period_label=period_label,
        notes=(notes or "").strip(),
        status=ProgressReport.Status.SUBMITTED,
        submitted_at=timezone.now(),
    )
    if file:
        create_kwargs["file"] = file
    if extra_fields:
        for field, val in extra_fields.items():
            create_kwargs[field] = val

    report = ProgressReport.objects.create(**create_kwargs)

    # Save inline formsets if provided
    for formset in filter(None, [supervisor_formset, skills_formset, manuscript_formset,
                                  conference_formset, presentation_formset]):
        if hasattr(formset, "is_valid") and formset.is_valid():
            instances = formset.save(commit=False)
            for obj in instances:
                obj.report = report
                obj.save()
            for obj in formset.deleted_objects:
                obj.delete()

    log_audit(
        actor=actor,
        action="create",
        target_app="rims_scholarships",
        target_model="ProgressReport",
        object_id=report.pk,
        object_repr=f"{scholar} — {period_label}",
        changes={"period_label": period_label, "status": "submitted"},
    )
    return report


@transaction.atomic
def submit_graduation(
    scholar: Scholar,
    *,
    graduated_on,
    certificate_file=None,
    notes: str = "",
    actor=None,
) -> GraduationRecord:
    """Scholar self-service graduation submission.

    Creates a GraduationRecord which triggers the signal that auto-creates
    the AlumniProfile. Idempotent — returns existing record if already submitted.
    """
    existing = GraduationRecord.objects.filter(scholar=scholar).first()
    if existing:
        return existing

    record = GraduationRecord(
        scholar=scholar,
        graduated_on=graduated_on,
        notes=(notes or "").strip(),
    )
    if certificate_file:
        record.certificate_file = certificate_file
    record.save()  # signal fires here → AlumniProfile created

    log_audit(
        actor=actor,
        action="create",
        target_app="rims_scholarships",
        target_model="GraduationRecord",
        object_id=record.pk,
        object_repr=f"{scholar} — graduated {graduated_on}",
        changes={"graduated_on": str(graduated_on), "submitted_by": "scholar_self_service"},
    )
    return record


@transaction.atomic
def process_stipend(
    scholar: Scholar,
    *,
    amount: Decimal,
    currency: str,
    paid_on,
    reference: str = "",
    actor=None,
) -> Stipend:
    """Create a Stipend payment record for *scholar* and audit it."""
    if amount is None or amount <= 0:
        raise ValidationError({"amount": "Stipend amount must be greater than zero."})
    # P0 (assessment §3 #9) — never post a stipend against a withdrawn scholar;
    # the lifecycle is terminal for new payments. Reactivation is required first.
    if scholar.status == Scholar.Status.WITHDRAWN:
        raise ValidationError(
            "Cannot record a stipend for a withdrawn scholar. Reactivate the "
            "scholar first if payment is genuinely owed."
        )
    try:
        stipend = Stipend.objects.create(
            scholar=scholar,
            amount=amount,
            currency=currency,
            paid_on=paid_on,
            reference=(reference or "").strip(),
        )
    except IntegrityError as exc:
        raise ValidationError("Could not record stipend.") from exc
    log_audit(
        actor=actor,
        action="create",
        target_app="rims_scholarships",
        target_model="Stipend",
        object_id=stipend.pk,
        object_repr=f"{scholar} — {amount} {currency}",
        changes={"amount": str(amount), "currency": currency, "paid_on": str(paid_on)},
    )
    return stipend
