from datetime import date
from decimal import Decimal

import pytest

from apps.mel.indicators.models import (
    IndicatorFrequency,
    IndicatorTarget,
    LogFrame,
    LogFrameLevel,
    LogFrameRow,
    Indicator,
    RagStatus,
)
from apps.mel.indicators.services import (
    calculate_progress,
    record_data_point,
    validate_hierarchy,
)

pytestmark = pytest.mark.django_db


def _build_indicator(target_value=Decimal("10"), baseline=Decimal("0")):
    lf = LogFrame.objects.create(name="LF", slug="lf")
    impact = LogFrameRow.objects.create(logframe=lf, level=LogFrameLevel.IMPACT, title="Impact")
    outcome = LogFrameRow.objects.create(logframe=lf, level=LogFrameLevel.OUTCOME, title="Outcome", parent=impact)
    output = LogFrameRow.objects.create(logframe=lf, level=LogFrameLevel.OUTPUT, title="Output", parent=outcome)
    ind = Indicator.objects.create(
        logframe_row=output,
        code="test-ind",
        name="Test Indicator",
        data_source="test",
        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=baseline,
        target_value=target_value,
    )
    return ind


def test_record_data_point_is_idempotent_on_source_object():
    ind = _build_indicator()
    r1 = record_data_point(
        indicator=ind,
        value=5,
        period_label="2026-04",
        source_module="rims",
        source_event="award_created",
        source_object_id="42",
        auto_verify=True,
    )
    r2 = record_data_point(
        indicator=ind,
        value=5,
        period_label="2026-04",
        source_module="rims",
        source_event="award_created",
        source_object_id="42",
        auto_verify=True,
    )
    assert r1.created is True
    assert r2.created is False
    assert r1.data_point.pk == r2.data_point.pk


def test_calculate_progress_rag_thresholds():
    ind = _build_indicator(target_value=Decimal("10"))
    record_data_point(indicator=ind, value=9, period_label="2026-04", auto_verify=True, source_module="m", source_event="e", source_object_id="a")
    p = calculate_progress(ind, "2026-04")
    assert p["rag"] == RagStatus.GREEN
    assert p["percent"] == pytest.approx(90.0)

    # Drop it to amber territory.
    record_data_point(indicator=ind, value=-2, period_label="2026-04", auto_verify=True, source_module="m", source_event="e", source_object_id="b")
    p = calculate_progress(ind, "2026-04")
    assert p["rag"] == RagStatus.AMBER

    record_data_point(indicator=ind, value=-4, period_label="2026-04", auto_verify=True, source_module="m", source_event="e", source_object_id="c")
    p = calculate_progress(ind, "2026-04")
    assert p["rag"] == RagStatus.RED


def test_validate_hierarchy_flags_dangling_outputs():
    lf = LogFrame.objects.create(name="LF2", slug="lf2")
    impact = LogFrameRow.objects.create(logframe=lf, level=LogFrameLevel.IMPACT, title="I")
    # Outcome directly under Impact — fine.
    LogFrameRow.objects.create(logframe=lf, level=LogFrameLevel.OUTCOME, title="O", parent=impact)
    # An Activity parented directly under Impact skips Outcome + Output.
    LogFrameRow.objects.create(
        logframe=lf,
        level=LogFrameLevel.ACTIVITY,
        title="Rogue activity",
        parent=impact,
    )
    issues = validate_hierarchy(lf)
    assert any("Activity 'Rogue activity'" in m for m in issues)
