"""PRD §5.4 FRFM020/021/023/025 — variance + cost analytics detail rows."""
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, Expenditure
from apps.rims.finance.services_analytics import (
    cashflow_lookback,
    cost_centre_breakdown,
    variance_detail_rows,
)
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) -> Budget:
    call = GrantCall.objects.create(
        title=f"6D-var call {timezone.now().timestamp()}",
        slug=f"6d-var-{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="Variance budget", currency="USD")
    BudgetLine.objects.create(
        budget=budget,
        label="Personnel",
        amount=Decimal("6000"),
        category=BudgetLine.Category.PERSONNEL,
    )
    BudgetLine.objects.create(
        budget=budget,
        label="Travel",
        amount=Decimal("4000"),
        category=BudgetLine.Category.TRAVEL,
    )
    return budget


@pytest.mark.django_db
def test_variance_detail_rows_per_line(applicant_user, institution):
    """PRD §5.4 FRFM020 — one row per BudgetLine with budgeted/expended/remaining/pct."""
    budget = _budget(applicant_user, institution)
    line = budget.lines.first()
    Expenditure.objects.create(
        budget_line=line, amount=Decimal("1500"), incurred_on=timezone.now().date()
    )
    rows = variance_detail_rows(budget)
    assert len(rows) == 2
    p_row = next(r for r in rows if r["line"].label == "Personnel")
    assert p_row["budgeted"] == Decimal("6000")
    assert p_row["expended"] == Decimal("1500")
    assert p_row["remaining"] == Decimal("4500")
    assert p_row["pct_used"] == 25.0


@pytest.mark.django_db
def test_cashflow_lookback_trims_to_window(applicant_user, institution):
    """PRD §5.4 FRFM023 — N-month look-back returns at most N buckets."""
    budget = _budget(applicant_user, institution)
    rows = cashflow_lookback(budget, months=3)
    assert len(rows) <= 3


@pytest.mark.django_db
def test_cost_centre_breakdown_groups_by_category(applicant_user, institution):
    """PRD §5.4 FRFM025 — group by BudgetLine.category."""
    budget = _budget(applicant_user, institution)
    Expenditure.objects.create(
        budget_line=budget.lines.first(),
        amount=Decimal("500"),
        incurred_on=timezone.now().date(),
    )
    rows = cost_centre_breakdown(budget)
    cats = {r["category"] for r in rows}
    assert "personnel" in cats
    assert "travel" in cats
    personnel = next(r for r in rows if r["category"] == "personnel")
    assert personnel["budgeted"] == Decimal("6000")
    assert personnel["expended"] == Decimal("500")
