"""PRD §5.1 FRFA-CO006 — awardee-facing final-report comment loop."""
from __future__ import annotations

from datetime import timedelta
from decimal import Decimal

import pytest
from django.core.exceptions import ValidationError
from django.urls import reverse
from django.utils import timezone

from apps.core.authentication.models import MFADevice
from apps.rims.grants.closeout_helpers import (
    get_or_create_closeout_record,
    request_narrative_revision,
    raise_financial_queries,
    resubmit_financial_report,
    resubmit_narrative_report,
)
from apps.rims.grants.models import (
    Application,
    Award,
    FinalFinancialReport,
    FinalNarrativeReport,
    GrantCall,
)
from apps.rims.grants.services import (
    award_application,
    shortlist_application,
    submit_application,
)


def _build_award(applicant_user, institution) -> Award:
    call = GrantCall.objects.create(
        title="CO006 resubmit call",
        slug=f"co006-{int(timezone.now().timestamp() * 1000) % 10_000_000}",
        opens_at=timezone.now() - timedelta(days=1),
        closes_at=timezone.now() + timedelta(days=10),
        status=GrantCall.Status.PUBLISHED,
    )
    app = Application.objects.create(call=call, applicant=applicant_user, institution=institution)
    submit_application(app)
    app = Application.objects.get(pk=app.pk)
    shortlist_application(app)
    award_application(
        app,
        Decimal("1000"),
        timezone.now().date() + timedelta(days=180),
        narrative="",
    )
    return Application.objects.get(pk=app.pk).award


@pytest.mark.django_db
def test_awardee_can_resubmit_narrative_after_revision_request(
    client, applicant_user, grants_manager_user, institution, monkeypatch
):
    """PRD §5.1 FRFA-CO006 — REVISION_REQUIRED → SUBMITTED via awardee POST."""
    award = _build_award(applicant_user, institution)
    closeout = get_or_create_closeout_record(award)
    report = FinalNarrativeReport.objects.create(closeout=closeout, notes="first attempt")
    # Manager raises a revision request.
    request_narrative_revision(report, comment="Needs more detail on outputs.", actor=grants_manager_user)
    report = FinalNarrativeReport.objects.get(pk=report.pk)
    assert report.status == FinalNarrativeReport.Status.REVISION_REQUIRED

    client.force_login(applicant_user)
    resp = client.post(
        reverse("rims_grants:final_narrative_resubmit", kwargs={"pk": report.pk}),
        {"response_note": "Added outputs and beneficiaries section."},
    )
    assert resp.status_code in {302, 303}, resp.status_code
    report = FinalNarrativeReport.objects.get(pk=report.pk)
    assert report.status == FinalNarrativeReport.Status.SUBMITTED
    assert report.response_note == "Added outputs and beneficiaries section."


@pytest.mark.django_db
def test_awardee_can_resubmit_financial_after_queries_raised(
    client, applicant_user, finance_user, institution
):
    """PRD §5.1 FRFA-CO006 — QUERIES_RAISED → SUBMITTED via awardee POST."""
    award = _build_award(applicant_user, institution)
    closeout = get_or_create_closeout_record(award)
    report = FinalFinancialReport.objects.create(closeout=closeout, notes="initial reconciliation")
    raise_financial_queries(report, queries="Receipt for line 3 missing.", actor=finance_user)
    report = FinalFinancialReport.objects.get(pk=report.pk)
    assert report.status == FinalFinancialReport.Status.QUERIES_RAISED

    client.force_login(applicant_user)
    resp = client.post(
        reverse("rims_grants:final_financial_resubmit", kwargs={"pk": report.pk}),
        {"response_note": "Receipt attached as appendix B."},
    )
    assert resp.status_code in {302, 303}
    report = FinalFinancialReport.objects.get(pk=report.pk)
    assert report.status == FinalFinancialReport.Status.SUBMITTED
    assert "appendix B" in report.response_note


@pytest.mark.django_db
def test_non_applicant_cannot_resubmit(client, applicant_user, grants_manager_user, institution):
    """PRD §5.1 FRFA-CO006 — object-level permission: only the applicant may resubmit."""
    award = _build_award(applicant_user, institution)
    closeout = get_or_create_closeout_record(award)
    report = FinalNarrativeReport.objects.create(closeout=closeout, notes="first attempt")
    request_narrative_revision(report, comment="please revise", actor=grants_manager_user)

    # Privileged roles need a verified MFA device or the middleware redirects
    # every POST to enrolment — bypass so the 403 we want to test surfaces.
    MFADevice.objects.get_or_create(
        user=grants_manager_user,
        device_type=MFADevice.DeviceType.TOTP,
        defaults={"name": "Test TOTP", "secret": "JBSWY3DPEHPK3PXP", "is_verified": True},
    )

    client.force_login(grants_manager_user)
    resp = client.post(
        reverse("rims_grants:final_narrative_resubmit", kwargs={"pk": report.pk}),
        {"response_note": "tampering"},
    )
    assert resp.status_code == 403


@pytest.mark.django_db
def test_resubmit_from_accepted_raises_validation_error(applicant_user, grants_manager_user, institution):
    """Cannot resubmit a terminal-state report (FSM enforces it; service surfaces it)."""
    award = _build_award(applicant_user, institution)
    closeout = get_or_create_closeout_record(award)
    report = FinalNarrativeReport.objects.create(closeout=closeout)
    report.accept()
    report.save()
    report = FinalNarrativeReport.objects.get(pk=report.pk)
    assert report.status == FinalNarrativeReport.Status.ACCEPTED
    with pytest.raises(ValidationError):
        resubmit_narrative_report(report, response_note="trying", actor=applicant_user)


@pytest.mark.django_db
def test_resubmit_financial_from_revision_required_also_works(applicant_user, finance_user, institution):
    """PRD §5.1 FRFA-CO006 — financial resubmit allowed from REVISION_REQUIRED as well."""
    award = _build_award(applicant_user, institution)
    closeout = get_or_create_closeout_record(award)
    report = FinalFinancialReport.objects.create(closeout=closeout)
    report.request_revision()
    report.save()
    report = FinalFinancialReport.objects.get(pk=report.pk)
    assert report.status == FinalFinancialReport.Status.REVISION_REQUIRED
    resubmit_financial_report(report, response_note="reconciled", actor=applicant_user)
    report = FinalFinancialReport.objects.get(pk=report.pk)
    assert report.status == FinalFinancialReport.Status.SUBMITTED
    assert report.response_note == "reconciled"
