"""SRS §4.1.7 "Implement Activities & Report Progress" — closes the audit's
"no auto-propagation from activity progress to indicator values" gap.
record_activity_progress() pushes the parent output's average activity
percent-complete into any linked mel_indicators.Indicator as a DataPoint,
reusing mel's own (already-idempotent) record_data_point() service.
"""
from datetime import date, timedelta
from decimal import Decimal

import pytest
from django.utils import timezone

from apps.mel.indicators.models import DataPoint
from apps.rims.grants.models import (
    Application,
    GrantCall,
    ImplementationPlan,
    ImplementationPlanActivity,
    ImplementationPlanIndicator,
    ImplementationPlanOutcome,
    ImplementationPlanOutput,
)
from apps.rims.grants.services import (
    approve_implementation_plan,
    award_application,
    record_activity_progress,
    shortlist_application,
    submit_application,
)


def _approved_plan_with_output(applicant_user, institution, *, suffix="prog"):
    call = GrantCall.objects.create(
        title="Activity progress call",
        slug=f"prog-{suffix}-{int(timezone.now().timestamp())}",
        opens_at=timezone.now() - timedelta(days=1),
        closes_at=timezone.now() + timedelta(days=10),
        status=GrantCall.Status.PUBLISHED,
    )
    app = Application.objects.create(call=call, applicant=applicant_user, institution=institution)
    submit_application(app)
    app = Application.objects.get(pk=app.pk)
    shortlist_application(app)
    award_application(app, Decimal("1000"), date.today() + timedelta(days=180), narrative="")
    award = Application.objects.get(pk=app.pk).award

    plan = ImplementationPlan.objects.create(award=award, objective="Objective")
    outcome = ImplementationPlanOutcome.objects.create(plan=plan, title="Outcome")
    output = ImplementationPlanOutput.objects.create(outcome=outcome, title="Output")
    ImplementationPlanIndicator.objects.create(
        output=output, name="Output completion",
        baseline_value=Decimal("0"), target_value=Decimal("100"),
        means_of_verification=ImplementationPlanIndicator.MeansOfVerification.MONITORING_REPORT,
        reporting_frequency=ImplementationPlanIndicator.ReportingFrequency.MONTHLY,
    )
    activity_a = ImplementationPlanActivity.objects.create(output=output, title="Activity A")
    activity_b = ImplementationPlanActivity.objects.create(output=output, title="Activity B")

    plan.submit()
    plan.save()
    approve_implementation_plan(plan, actor=applicant_user)
    return output, activity_a, activity_b


@pytest.mark.django_db
def test_activity_progress_creates_data_point(applicant_user, institution):
    output, activity_a, activity_b = _approved_plan_with_output(applicant_user, institution, suffix="create")
    indicator = output.indicators.first().mel_indicator

    # Set B's progress directly (not via record_activity_progress, which
    # would itself write a DataPoint and immediately trigger the same-period
    # dedup this test isn't exercising) so a single record_activity_progress
    # call on A reflects both activities' current state in one average.
    ImplementationPlanActivity.objects.filter(pk=activity_b.pk).update(percent_complete=50)

    record_activity_progress(activity_a, percent_complete=100, status=ImplementationPlanActivity.Status.COMPLETED)

    dp = DataPoint.objects.filter(indicator=indicator, source_module="rims_grants").order_by("-reported_at").first()
    assert dp is not None
    # Average of 100 (A) and 50 (B) = 75.
    assert dp.value == Decimal("75.0000")


@pytest.mark.django_db
def test_activity_progress_updates_within_new_period_only(applicant_user, institution):
    output, activity_a, activity_b = _approved_plan_with_output(applicant_user, institution, suffix="period")
    indicator = output.indicators.first().mel_indicator

    # activity_b stays at its default 0% throughout, so the recorded value
    # is always avg(activity_a.percent_complete, 0).
    record_activity_progress(activity_a, percent_complete=20)
    first_count = DataPoint.objects.filter(indicator=indicator, source_module="rims_grants").count()
    assert first_count == 1
    first_dp = DataPoint.objects.filter(indicator=indicator, source_module="rims_grants").first()
    assert first_dp.value == Decimal("10.0000")

    # A second call in the *same* period is deduped by mel's own
    # record_data_point() (idempotent on indicator+source_module+
    # source_object_id) — no new row, and the original value is preserved,
    # even though activity_a's own percent_complete keeps advancing.
    record_activity_progress(activity_a, percent_complete=90)
    second_count = DataPoint.objects.filter(indicator=indicator, source_module="rims_grants").count()
    assert second_count == 1
    dp = DataPoint.objects.filter(indicator=indicator, source_module="rims_grants").first()
    assert dp.value == Decimal("10.0000")

    # But the activity's own record IS updated regardless of the
    # indicator-side dedup.
    activity_a = ImplementationPlanActivity.objects.get(pk=activity_a.pk)
    assert activity_a.percent_complete == 90


@pytest.mark.django_db
def test_percent_complete_clamped_to_0_100(applicant_user, institution):
    output, activity_a, _activity_b = _approved_plan_with_output(applicant_user, institution, suffix="clamp")
    record_activity_progress(activity_a, percent_complete=150)
    activity_a = ImplementationPlanActivity.objects.get(pk=activity_a.pk)
    assert activity_a.percent_complete == 100

    record_activity_progress(activity_a, percent_complete=-10)
    activity_a = ImplementationPlanActivity.objects.get(pk=activity_a.pk)
    assert activity_a.percent_complete == 0
