"""PRD §5.4 FRFM027 — monthly reforecast Celery task + immutability guarantee."""
from __future__ import annotations

from datetime import timedelta
from decimal import Decimal
from unittest.mock import patch

import pytest
from django.utils import timezone

from apps.core.audit.tasks import persist_audit_log
from apps.rims.finance.models import Budget, BudgetLine, Reforecast
from apps.rims.finance.tasks import recompute_reforecasts
from apps.rims.grants.models import Application, Award, GrantCall


@pytest.fixture(autouse=True)
def _sync_audit():
    with patch("apps.core.audit.tasks.write_audit_log.delay", side_effect=lambda **kw: persist_audit_log(**kw)):
        with patch(
            "apps.core.audit.mixins.transaction.on_commit",
            side_effect=lambda fn: fn(),
        ):
            yield


def _budget(applicant_user, institution, status=Budget.Status.APPROVED) -> Budget:
    call = GrantCall.objects.create(
        title=f"6D-celery call {timezone.now().timestamp()}",
        slug=f"6d-celery-{int(timezone.now().timestamp() * 1000) % 10_000_000}",
        call_type="grant",
        opens_at=timezone.now() - timedelta(days=200),
        closes_at=timezone.now() + timedelta(days=10),
        status=GrantCall.Status.PUBLISHED,
    )
    app = Application.objects.create(call=call, applicant=applicant_user, institution=institution)
    award = Award.objects.create(
        application=app,
        amount=Decimal("10000"),
        currency="USD",
        awarded_at=timezone.now(),
        project_end_date=timezone.now().date() + timedelta(days=365),
    )
    budget = Budget.objects.create(
        award=award, name="Celery budget", currency="USD", status=status,
    )
    BudgetLine.objects.create(budget=budget, label="L", amount=Decimal("10000"))
    return budget


@pytest.mark.django_db
def test_recompute_reforecasts_processes_approved_budgets(applicant_user, institution):
    """PRD §5.4 FRFM027 — beat iterates APPROVED and FINALIZED budgets."""
    approved = _budget(applicant_user, institution, status=Budget.Status.APPROVED)
    draft = _budget(applicant_user, institution, status=Budget.Status.DRAFT)
    count = recompute_reforecasts()
    assert count == 1  # only the APPROVED budget produced a snapshot
    assert Reforecast.objects.filter(budget=approved).exists()
    assert not Reforecast.objects.filter(budget=draft).exists()


@pytest.mark.django_db
def test_reforecasts_are_append_only_on_rerun(applicant_user, institution):
    """PRD §5.4 FRFM027 — re-running the beat appends a fresh snapshot rather
    than overwriting existing rows."""
    budget = _budget(applicant_user, institution)
    recompute_reforecasts()
    first_count = Reforecast.objects.filter(budget=budget).count()
    assert first_count == 1
    recompute_reforecasts()
    second_count = Reforecast.objects.filter(budget=budget).count()
    assert second_count == 2
