"""PRD §5.1 FRFA-GPS027 — FundingRecord budget revision workflow."""
from datetime import date, timedelta
from decimal import Decimal

import pytest
from django.core.exceptions import ValidationError
from django_fsm import TransitionNotAllowed

from apps.rims.grants.models import FundingRecord, FundingRecordBudgetRevision
from apps.rims.grants.services import (
    approve_funding_revision,
    create_funding_revision,
    reject_funding_revision,
)


def _approved_fr():
    return FundingRecord.objects.create(
        title="Approved FR",
        amount=Decimal("5000"),
        start_date=date.today(),
        end_date=date.today() + timedelta(days=365),
        status=FundingRecord.Status.APPROVED,
    )


@pytest.mark.django_db
def test_create_revision_captures_previous_terms(grants_manager_user):
    fr = _approved_fr()
    rev = create_funding_revision(
        fr,
        change_summary="Extend end date",
        justification="Operational delay",
        new_terms={"end_date": (date.today() + timedelta(days=730)).isoformat()},
        actor=grants_manager_user,
    )
    assert rev.revision_number == 1
    assert rev.previous_terms["amount"] == "5000"
    assert rev.status == FundingRecordBudgetRevision.Status.REQUESTED


@pytest.mark.django_db
def test_create_revision_blocks_on_draft_record(grants_manager_user):
    fr = FundingRecord.objects.create(
        title="Draft FR",
        amount=Decimal("100"),
        start_date=date.today(),
        end_date=date.today() + timedelta(days=30),
        status=FundingRecord.Status.DRAFT,
    )
    with pytest.raises(ValidationError):
        create_funding_revision(
            fr,
            change_summary="Nope",
            new_terms={},
            actor=grants_manager_user,
        )


@pytest.mark.django_db
def test_approve_revision_applies_terms(grants_manager_user):
    fr = _approved_fr()
    new_end = (date.today() + timedelta(days=730)).isoformat()
    rev = create_funding_revision(
        fr,
        change_summary="Extend end date",
        new_terms={"end_date": new_end, "amount": "7500"},
        actor=grants_manager_user,
    )
    approve_funding_revision(rev, actor=grants_manager_user)
    fr.refresh_from_db()
    rev = FundingRecordBudgetRevision.objects.get(pk=rev.pk)
    assert rev.status == FundingRecordBudgetRevision.Status.APPROVED
    assert fr.end_date.isoformat() == new_end
    assert fr.amount == Decimal("7500")


@pytest.mark.django_db
def test_reject_revision_preserves_fr_terms(grants_manager_user):
    fr = _approved_fr()
    original_end = fr.end_date
    rev = create_funding_revision(
        fr,
        change_summary="Decline",
        new_terms={"end_date": (date.today() + timedelta(days=730)).isoformat()},
        actor=grants_manager_user,
    )
    reject_funding_revision(rev, reason="Out of scope", actor=grants_manager_user)
    fr.refresh_from_db()
    rev = FundingRecordBudgetRevision.objects.get(pk=rev.pk)
    assert rev.status == FundingRecordBudgetRevision.Status.REJECTED
    assert fr.end_date == original_end


@pytest.mark.django_db
def test_reject_requires_reason(grants_manager_user):
    fr = _approved_fr()
    rev = create_funding_revision(
        fr, change_summary="x", new_terms={}, actor=grants_manager_user
    )
    with pytest.raises(ValidationError):
        reject_funding_revision(rev, reason="", actor=grants_manager_user)


@pytest.mark.django_db
def test_terminal_revisions_cannot_be_redecided(grants_manager_user):
    fr = _approved_fr()
    rev = create_funding_revision(
        fr, change_summary="x", new_terms={}, actor=grants_manager_user
    )
    approve_funding_revision(rev, actor=grants_manager_user)
    rev = FundingRecordBudgetRevision.objects.get(pk=rev.pk)
    with pytest.raises(TransitionNotAllowed):
        rev.reject()
