"""PRD §5.4 FRFM016 / NFRFM002 — multi-currency support tests."""

from __future__ import annotations

from datetime import date, timedelta
from decimal import Decimal

import pytest
from django.core.exceptions import ValidationError

from apps.rims.finance.models import Budget, BudgetLine, ExchangeRate
from apps.rims.finance.services import (
    MissingExchangeRateError,
    convert,
    finance_health_snapshot,
)
from apps.rims.grants.models import Application, Award, GrantCall


@pytest.fixture
def usd_award(applicant_user, institution):
    call = GrantCall.objects.create(
        title="USD call",
        slug="mc-usd",
        opens_at=date.today() - timedelta(days=1),
        closes_at=date.today() + timedelta(days=30),
        status=GrantCall.Status.PUBLISHED,
    )
    app = Application.objects.create(call=call, applicant=applicant_user, institution=institution)
    return Award.objects.create(
        application=app,
        amount=Decimal("1000"),
        currency="USD",
        project_end_date=date.today() + timedelta(days=180),
        status=Award.Status.ACTIVE,
        activation_date=date.today() - timedelta(days=10),
    )


# ---------------------------------------------------------------------------
# convert()
# ---------------------------------------------------------------------------
@pytest.mark.django_db
def test_convert_same_currency_is_noop():
    assert convert(Decimal("100"), "USD", "USD") == Decimal("100")


@pytest.mark.django_db
def test_convert_uses_latest_effective_rate():
    ExchangeRate.objects.create(
        from_currency="USD",
        to_currency="UGX",
        rate=Decimal("3700"),
        effective_date=date.today() - timedelta(days=5),
    )
    ExchangeRate.objects.create(
        from_currency="USD",
        to_currency="UGX",
        rate=Decimal("3800"),
        effective_date=date.today() - timedelta(days=1),
    )
    assert convert(Decimal("10"), "USD", "UGX") == Decimal("38000.00")


@pytest.mark.django_db
def test_convert_falls_back_to_inverse_pair():
    ExchangeRate.objects.create(
        from_currency="USD",
        to_currency="UGX",
        rate=Decimal("3800"),
        effective_date=date.today() - timedelta(days=1),
    )
    # No UGX → USD row, but we should derive from the inverse.
    result = convert(Decimal("38000"), "UGX", "USD")
    assert result == Decimal("10.00")


@pytest.mark.django_db
def test_convert_raises_when_no_rate_exists():
    with pytest.raises(MissingExchangeRateError):
        convert(Decimal("10"), "USD", "EUR")


@pytest.mark.django_db
def test_convert_respects_as_of_date():
    ExchangeRate.objects.create(
        from_currency="USD",
        to_currency="UGX",
        rate=Decimal("3500"),
        effective_date=date.today() - timedelta(days=30),
    )
    ExchangeRate.objects.create(
        from_currency="USD",
        to_currency="UGX",
        rate=Decimal("3800"),
        effective_date=date.today() - timedelta(days=1),
    )
    historical = convert(
        Decimal("10"),
        "USD",
        "UGX",
        as_of=date.today() - timedelta(days=20),
    )
    assert historical == Decimal("35000.00")


# ---------------------------------------------------------------------------
# Budget.currency validation
# ---------------------------------------------------------------------------
@pytest.mark.django_db
def test_budget_currency_must_match_award(usd_award):
    budget = Budget(award=usd_award, name="Wrong-currency budget", currency="EUR")
    with pytest.raises(ValidationError):
        budget.full_clean()


# ---------------------------------------------------------------------------
# finance_health_snapshot honours report_currency
# ---------------------------------------------------------------------------
@pytest.mark.django_db
def test_health_snapshot_converts_to_report_currency(usd_award):
    budget = Budget.objects.create(
        award=usd_award,
        name="USD budget",
        currency="USD",
        status=Budget.Status.APPROVED,
    )
    BudgetLine.objects.create(budget=budget, label="Line", amount=Decimal("100"))
    ExchangeRate.objects.create(
        from_currency="USD",
        to_currency="UGX",
        rate=Decimal("3800"),
        effective_date=date.today() - timedelta(days=1),
    )

    native = finance_health_snapshot(budget)
    converted = finance_health_snapshot(budget, report_currency="UGX")

    assert native["budget_ceiling"] == Decimal("100")
    assert native["report_currency"] == "USD"
    assert converted["budget_ceiling"] == Decimal("380000.00")
    assert converted["report_currency"] == "UGX"
    assert converted["source_currency"] == "USD"
