"""G3 — Budget vs expenditure variance builder.

Verifies that ``build_budget_variance`` returns category-level utilisation and
RAG flags wired through the live finance analytics helpers.
"""
from __future__ import annotations

from datetime import date, timedelta
from decimal import Decimal

import pytest
from django.contrib.auth import get_user_model

from apps.core.authentication.models import Institution
from apps.mel.indicators.models import LogFrame
from apps.mel.reports.builders import (
    BUDGET_VARIANCE_RED_THRESHOLD,
    build_budget_variance,
    build_consolidated_data,
)
from apps.rims.finance.models import (
    Budget,
    BudgetLine,
    DisbursementRequest,
    Expenditure,
)
from apps.rims.grants.models import Application, Award, GrantCall


pytestmark = pytest.mark.django_db

User = get_user_model()


@pytest.fixture
def bound_budget(db):
    institution = Institution.objects.create(name="G3 Test University", country="UG", is_member=True)
    applicant = User.objects.create_user(email="g3.applicant@example.com", password="pw-g3")
    applicant.institution = institution
    applicant.save()
    call = GrantCall.objects.create(
        title="G3 call",
        slug="g3-call",
        opens_at=date.today() - timedelta(days=10),
        closes_at=date.today() + timedelta(days=30),
        status=GrantCall.Status.PUBLISHED,
    )
    app = Application.objects.create(call=call, applicant=applicant, institution=institution)
    award = Award.objects.create(
        application=app,
        amount=Decimal("10000"),
        currency="USD",
        activation_date=date.today() - timedelta(days=30),
        project_end_date=date.today() + timedelta(days=70),
        status=Award.Status.ACTIVE,
    )
    budget = Budget.objects.create(
        award=award,
        name="G3 budget",
        currency="USD",
        status=Budget.Status.APPROVED,
    )
    personnel_line = BudgetLine.objects.create(
        budget=budget,
        label="Personnel",
        category=BudgetLine.Category.PERSONNEL,
        amount=Decimal("4000"),
    )
    operations_line = BudgetLine.objects.create(
        budget=budget,
        label="Operations",
        category=BudgetLine.Category.OPERATIONS,
        amount=Decimal("2000"),
    )
    # Personnel: heavily overspent — 90% utilisation against ~30% elapsed → RED.
    Expenditure.objects.create(
        budget_line=personnel_line,
        amount=Decimal("3600"),
        incurred_on=date.today(),
    )
    # Operations: well within pacing.
    Expenditure.objects.create(
        budget_line=operations_line,
        amount=Decimal("200"),
        incurred_on=date.today(),
    )
    # A disbursement so compute_variance has something to compute.
    DisbursementRequest.objects.create(
        budget=budget,
        amount=Decimal("3800"),
        status=DisbursementRequest.Status.PAID,
    )
    return budget


@pytest.fixture
def logframe_with_budget(bound_budget):
    return LogFrame.objects.create(
        name="LF G3",
        slug="lf-g3",
        finance_budget=bound_budget,
    )


def test_build_budget_variance_returns_categories_with_utilisation(logframe_with_budget):
    out = build_budget_variance(logframe=logframe_with_budget)

    assert out["installed"] is True
    assert out["budget_name"] == "G3 budget"
    assert out["currency"] == "USD"
    assert out["rag_threshold_pct"] == int(BUDGET_VARIANCE_RED_THRESHOLD * 100)
    assert out["variance"] is not None
    assert out["variance"]["direction"] in {"over", "under", "on-track"}

    categories = {c["category"]: c for c in out["categories"]}
    assert "personnel" in categories
    assert "operations" in categories

    # Personnel utilisation = 3600 / 4000 = 0.9 → far above elapsed ratio → RED.
    personnel = categories["personnel"]
    assert personnel["utilisation"] == pytest.approx(0.9, rel=1e-3)
    assert personnel["rag"] == "red"
    assert out["flagged_categories"] >= 1


def test_build_budget_variance_returns_empty_for_logframe_without_budget():
    lf = LogFrame.objects.create(name="No budget", slug="no-budget")
    out = build_budget_variance(logframe=lf)
    assert out["installed"] is False
    assert out["categories"] == []
    assert out["variance"] is None


def test_consolidated_data_includes_budget_variance_when_section_requested(logframe_with_budget):
    today = date.today()
    data = build_consolidated_data(
        logframe=logframe_with_budget,
        period_label="2026Q2",
        period_start=today - timedelta(days=30),
        period_end=today,
        sections=["budget_variance"],
    )
    assert "budget_variance" in data
    assert data["budget_variance"]["installed"] is True
