"""PRD §5.1 FRFA-CO006/CO007 — Awardee-facing final report submission.

Before this view existed, the only writer of FinalNarrativeReport /
FinalFinancialReport was the Grants Manager's own close-out submission,
pre-populated as already ACCEPTED — making the FRFA-CO008–010 review FSM
unreachable dead code (calling `.accept()` on an already-ACCEPTED FSM field
raises TransitionNotAllowed). These tests exercise the real submit → review
→ accept path end to end.
"""
from __future__ import annotations

from datetime import timedelta
from decimal import Decimal

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

from apps.rims.grants.closeout_helpers import (
    accept_financial_report,
    accept_narrative_report,
    get_or_create_closeout_record,
)
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/007 submit call",
        slug=f"co006-submit-{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


# Matches the minimal-but-libmagic-detectable PDF fixture pattern already
# used elsewhere in this test suite (e.g. test_call_setup_cs016_cs020.py).
_PDF_HEAD = b"%PDF-1.4\n%\xc3\xa4\xc3\xb6\n"


def _narrative_file():
    return SimpleUploadedFile("narrative.pdf", _PDF_HEAD + b"fake narrative\n%%EOF\n", content_type="application/pdf")


def _financial_file():
    return SimpleUploadedFile("financial.pdf", _PDF_HEAD + b"fake financial\n%%EOF\n", content_type="application/pdf")


@pytest.mark.django_db
def test_awardee_can_submit_final_reports_in_submitted_status(client, applicant_user, institution):
    award = _build_award(applicant_user, institution)
    Award.objects.filter(pk=award.pk).update(status=Award.Status.CLOSEOUT_IN_PROGRESS)

    client.force_login(applicant_user)
    resp = client.post(
        reverse("rims_grants:award_final_report_submit", kwargs={"pk": award.pk}),
        {
            "narrative-report": _narrative_file(),
            "narrative-notes": "Outcomes narrative for the award.",
            "financial-report": _financial_file(),
            "financial-notes": "Reconciled all disbursed tranches.",
        },
        follow=True,
    )
    assert resp.status_code == 200

    closeout = get_or_create_closeout_record(Award.objects.get(pk=award.pk))
    narrative = closeout.narrative_reports.get()
    financial = closeout.financial_reports.get()
    # Not pre-accepted — a real SUBMITTED row the review FSM can act on.
    assert narrative.status == FinalNarrativeReport.Status.SUBMITTED
    assert financial.status == FinalFinancialReport.Status.SUBMITTED
    assert narrative.submitted_by_id == applicant_user.id
    assert financial.submitted_by_id == applicant_user.id
    assert narrative.report.name
    assert financial.report.name


@pytest.mark.django_db
def test_submit_view_rejects_non_applicant(client, applicant_user, grants_manager_user, institution):
    award = _build_award(applicant_user, institution)
    Award.objects.filter(pk=award.pk).update(status=Award.Status.CLOSEOUT_IN_PROGRESS)

    client.force_login(grants_manager_user)
    resp = client.post(
        reverse("rims_grants:award_final_report_submit", kwargs={"pk": award.pk}),
        {
            "narrative-report": _narrative_file(),
            "narrative-notes": "x",
            "financial-report": _financial_file(),
            "financial-notes": "y",
        },
    )
    assert resp.status_code == 403


@pytest.mark.django_db
def test_submit_blocked_before_closeout_initiated(client, applicant_user, institution):
    """Award is still ACTIVE — close-out hasn't begun, so reports cannot be submitted yet."""
    award = _build_award(applicant_user, institution)
    assert award.status == Award.Status.ACTIVE

    client.force_login(applicant_user)
    resp = client.post(
        reverse("rims_grants:award_final_report_submit", kwargs={"pk": award.pk}),
        {
            "narrative-report": _narrative_file(),
            "narrative-notes": "x",
            "financial-report": _financial_file(),
            "financial-notes": "y",
        },
        follow=True,
    )
    assert resp.status_code == 200
    closeout = get_or_create_closeout_record(Award.objects.get(pk=award.pk))
    assert closeout.narrative_reports.count() == 0
    assert closeout.financial_reports.count() == 0


@pytest.mark.django_db
def test_full_submit_review_accept_path(client, applicant_user, grants_manager_user, finance_user, institution):
    """PRD §5.1 FRFA-CO006–010 — the whole loop is reachable against a real
    submitted report: submit → Finance/GM review → accept."""
    award = _build_award(applicant_user, institution)
    Award.objects.filter(pk=award.pk).update(status=Award.Status.CLOSEOUT_IN_PROGRESS)

    client.force_login(applicant_user)
    client.post(
        reverse("rims_grants:award_final_report_submit", kwargs={"pk": award.pk}),
        {
            "narrative-report": _narrative_file(),
            "narrative-notes": "Outcomes narrative.",
            "financial-report": _financial_file(),
            "financial-notes": "Reconciled tranches.",
        },
    )
    closeout = get_or_create_closeout_record(Award.objects.get(pk=award.pk))
    narrative = closeout.narrative_reports.get()
    financial = closeout.financial_reports.get()

    accept_narrative_report(narrative, actor=grants_manager_user, comment="Looks complete.")
    accept_financial_report(financial, actor=finance_user)

    narrative = FinalNarrativeReport.objects.get(pk=narrative.pk)
    financial = FinalFinancialReport.objects.get(pk=financial.pk)
    assert narrative.status == FinalNarrativeReport.Status.ACCEPTED
    assert financial.status == FinalFinancialReport.Status.ACCEPTED

    # Calling accept() again would previously be the only path (fabricated by
    # the GM's own close-out submission) and is now provably reachable via a
    # genuine awardee-submitted report instead of unreachable dead code.
    with pytest.raises(Exception):
        narrative.accept()
