"""End-to-end UI smoke test for the SRS §4.1.7 "Implementation & M&E" tab on
award_detail.html — covers template rendering with a full plan/RAG-progress
hierarchy plus the key POST actions (create plan, submit, approve, add
outcome/output/indicator/activity, record progress, schedule/lock a visit).
"""
from datetime import date, timedelta
from decimal import Decimal
from unittest.mock import patch

import pytest
from django.urls import reverse
from django.utils import timezone

from apps.rims.grants.models import (
    Application,
    GrantCall,
    ImplementationPlan,
    ImplementationPlanOutcome,
    ImplementationPlanOutput,
)
from apps.rims.grants.services import award_application, shortlist_application, submit_application


@pytest.fixture(autouse=True)
def _no_async_dispatch():
    with patch(
        "apps.core.notifications.services.dispatch_notification_channel.delay"
    ) as fake:
        yield fake


def _award_for(applicant_user, institution, *, suffix="uitab"):
    call = GrantCall.objects.create(
        title="M&E tab call",
        slug=f"uitab-{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="")
    return Application.objects.get(pk=app.pk).award


@pytest.mark.django_db
def test_award_detail_renders_me_tab_with_no_plan(client, grants_manager_user, applicant_user, institution):
    award = _award_for(applicant_user, institution, suffix="noplan")
    client.force_login(grants_manager_user)
    resp = client.get(reverse("rims_grants:award_detail", kwargs={"pk": award.pk}))
    assert resp.status_code == 200
    assert b"Implementation" in resp.content


@pytest.mark.django_db
def test_full_me_flow_through_views(client, grants_manager_user, applicant_user, institution):
    award = _award_for(applicant_user, institution, suffix="flow")
    client.force_login(grants_manager_user)

    resp = client.post(
        reverse("rims_grants:implementation_plan_create", kwargs={"pk": award.pk}),
        {"objective": "Improve smallholder yields", "assumptions": "Weather is favourable."},
    )
    assert resp.status_code == 302
    plan = ImplementationPlan.objects.get(award=award)
    assert plan.status == ImplementationPlan.Status.DRAFT

    resp = client.post(
        reverse("rims_grants:implementation_plan_outcome_create", kwargs={"pk": award.pk}),
        {"title": "Farmers adopt improved practices", "description": "", "order": 0},
    )
    assert resp.status_code == 302
    outcome = ImplementationPlanOutcome.objects.get(plan=plan)

    resp = client.post(
        reverse(
            "rims_grants:implementation_plan_output_create",
            kwargs={"pk": award.pk, "outcome_pk": outcome.pk},
        ),
        {"title": "Farmers trained", "description": "", "order": 0},
    )
    assert resp.status_code == 302
    output = ImplementationPlanOutput.objects.get(outcome=outcome)

    resp = client.post(
        reverse("rims_grants:implementation_plan_indicator_create", kwargs={"pk": award.pk}),
        {
            "output_pk": output.pk,
            "name": "Number of farmers trained",
            "baseline_value": "0",
            "target_value": "500",
            "means_of_verification": "attendance_register",
            "reporting_frequency": "monthly",
        },
    )
    assert resp.status_code == 302
    assert output.indicators.count() == 1

    resp = client.post(
        reverse(
            "rims_grants:implementation_plan_activity_create",
            kwargs={"pk": award.pk, "output_pk": output.pk},
        ),
        {"title": "Conduct training sessions", "risks": "", "mitigation_measures": ""},
    )
    assert resp.status_code == 302
    activity = output.activities.get()

    resp = client.post(reverse("rims_grants:implementation_plan_submit", kwargs={"pk": award.pk}))
    assert resp.status_code == 302
    plan = ImplementationPlan.objects.get(pk=plan.pk)
    assert plan.status == ImplementationPlan.Status.SUBMITTED

    resp = client.post(
        reverse("rims_grants:implementation_plan_decision", kwargs={"pk": award.pk}),
        {"action": "approve"},
    )
    assert resp.status_code == 302
    plan = ImplementationPlan.objects.get(pk=plan.pk)
    assert plan.status == ImplementationPlan.Status.APPROVED
    assert plan.results_framework_id is not None

    resp = client.post(
        reverse(
            "rims_grants:implementation_plan_activity_progress",
            kwargs={"pk": award.pk, "activity_pk": activity.pk},
        ),
        {"percent_complete": "40", "status": "in_progress"},
    )
    assert resp.status_code == 302

    resp = client.post(
        reverse("rims_grants:monitoring_visit_create", kwargs={"pk": award.pk}),
        {
            "visit_type": "physical",
            "scheduled_for": "2026-08-01T09:00",
            "participants": "Programme Officer, Awardee",
            "agenda": "Site inspection",
        },
    )
    assert resp.status_code == 302
    visit = award.monitoring_visits.get()

    resp = client.post(
        reverse("rims_grants:monitoring_visit_findings", kwargs={"pk": award.pk, "visit_pk": visit.pk}),
        {"findings": "All on track.", "latitude": "0.313611", "longitude": "32.581111"},
    )
    assert resp.status_code == 302

    resp = client.post(reverse("rims_grants:monitoring_visit_lock", kwargs={"pk": award.pk, "visit_pk": visit.pk}))
    assert resp.status_code == 302

    # Now render the full tab with a plan, RAG progress, and a locked visit —
    # this is the render path most likely to break on a template typo.
    resp = client.get(reverse("rims_grants:award_detail", kwargs={"pk": award.pk}))
    assert resp.status_code == 200
    assert b"Farmers adopt improved practices" in resp.content
    assert b"Locked" in resp.content
