"""PRD §6.1 / Phase 1 tracker — finance boundaries (budget ceiling, project closeout)."""

import uuid
from datetime import date, datetime, timedelta, timezone as py_tz
from decimal import Decimal
from unittest.mock import patch

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

from apps.rims.finance.models import Budget, BudgetLine, DisbursementRequest
from apps.rims.finance.services import (
    approve_disbursement_request,
    assert_award_period_allows_claims,
    submit_disbursement_request,
)
from apps.rims.grants.models import Application, GrantCall
from apps.rims.grants.services import award_application, shortlist_application, submit_application


def _award_budget_line(applicant_user, institution, *, project_end: date, line_amount: Decimal = Decimal("10000")):
    call = GrantCall.objects.create(
        title="Fin test call",
        slug=f"fin-test-{uuid.uuid4().hex[:10]}",
        opens_at=timezone.now() - timedelta(days=1),
        closes_at=timezone.now() + timedelta(days=30),
        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("5000"), project_end, narrative="")
    award = app.award
    budget = Budget.objects.create(award=award, name="T")
    BudgetLine.objects.create(budget=budget, label="L", amount=line_amount)
    return budget


@pytest.mark.django_db
def test_approve_disbursement_rejects_over_budget(applicant_user, institution):
    budget = _award_budget_line(applicant_user, institution, project_end=date.today() + timedelta(days=365))
    req = DisbursementRequest.objects.create(
        budget=budget,
        amount=Decimal("99999"),
        status=DisbursementRequest.Status.SUBMITTED,
    )
    with pytest.raises(ValidationError, match="exceeds"):
        approve_disbursement_request(req)


@pytest.mark.django_db
def test_disbursement_blocked_after_project_end_date(applicant_user, institution):
    budget = _award_budget_line(applicant_user, institution, project_end=date(2020, 1, 1))
    req = DisbursementRequest.objects.create(
        budget=budget,
        amount=Decimal("100"),
        status=DisbursementRequest.Status.DRAFT,
    )
    future = datetime(2025, 6, 15, 12, 0, tzinfo=py_tz.utc)
    with patch("apps.rims.finance.services.timezone.now", return_value=future):
        with pytest.raises(ValidationError, match="project period has ended"):
            submit_disbursement_request(req)


@pytest.mark.django_db
def test_assert_award_period_allows_claims_on_end_date(applicant_user, institution):
    budget = _award_budget_line(applicant_user, institution, project_end=date(2025, 6, 15))
    on_end = datetime(2025, 6, 15, 23, 0, tzinfo=py_tz.utc)
    with patch("apps.rims.finance.services.timezone.now", return_value=on_end):
        assert_award_period_allows_claims(budget)
