"""Tests for variance snapshot service (FRMFL028–029)."""
from __future__ import annotations

from datetime import date
from decimal import Decimal

import pytest

from apps.mel.indicators.models import (
    Indicator,
    IndicatorFrequency,
    IndicatorTarget,
    IndicatorVariance,
    LogFrame,
    LogFrameLevel,
    LogFrameRow,
)
from apps.mel.indicators.services import compute_variance, record_data_point

pytestmark = pytest.mark.django_db


def _indicator():
    lf = LogFrame.objects.create(name="LF-V", slug="lf-var")
    impact = LogFrameRow.objects.create(logframe=lf, level=LogFrameLevel.IMPACT, title="I")
    outcome = LogFrameRow.objects.create(logframe=lf, level=LogFrameLevel.OUTCOME, title="O", parent=impact)
    output = LogFrameRow.objects.create(logframe=lf, level=LogFrameLevel.OUTPUT, title="P", parent=outcome)
    ind = Indicator.objects.create(
        logframe_row=output,
        code="var-ind",
        name="I",
        data_source="m",
        unit="count",
        calculation_method="Count.",
        frequency=IndicatorFrequency.MONTHLY,
    )
    IndicatorTarget.objects.create(
        indicator=ind,
        period_label="2026-04",
        period_start=date(2026, 4, 1),
        period_end=date(2026, 4, 30),
        baseline_value=Decimal("0"),
        target_value=Decimal("100"),
    )
    return ind


def test_compute_variance_returns_percent_difference():
    ind = _indicator()
    record_data_point(
        indicator=ind,
        value=75,
        period_label="2026-04",
        auto_verify=True,
        source_module="m",
        source_event="e",
        source_object_id="1",
    )
    row = compute_variance(ind, "2026-04", cause_analysis="late onboarding")

    assert row.actual_value == Decimal("75")
    assert row.target_value == Decimal("100.0000")
    # (75 - 100) / 100 * 100 = -25
    assert row.variance_pct == Decimal("-25.0000")
    assert row.cause_analysis == "late onboarding"


def test_compute_variance_is_idempotent_per_period():
    ind = _indicator()
    record_data_point(
        indicator=ind,
        value=50,
        period_label="2026-04",
        auto_verify=True,
        source_module="m",
        source_event="e",
        source_object_id="1",
    )
    compute_variance(ind, "2026-04")
    record_data_point(
        indicator=ind,
        value=30,
        period_label="2026-04",
        auto_verify=True,
        source_module="m",
        source_event="e",
        source_object_id="2",
    )
    # Updating the same (indicator, period) must not create a second row.
    second = compute_variance(ind, "2026-04", adjustment="raised intake")

    assert IndicatorVariance.objects.filter(indicator=ind, period_label="2026-04").count() == 1
    assert second.actual_value == Decimal("80")
    assert second.adjustment == "raised intake"
