"""Tests for report builders (pure data consolidation)."""
from __future__ import annotations

from datetime import date
from decimal import Decimal

import pytest

from apps.mel.indicators.models import (
    ImpactEvaluation,
    Indicator,
    IndicatorFrequency,
    IndicatorTarget,
    LogFrame,
    LogFrameLevel,
    LogFrameRow,
    OutcomeAssessment,
)
from apps.mel.indicators.services import record_data_point
from apps.mel.reports.builders import (
    build_activity_summary,
    build_consolidated_data,
    build_impact_section,
    build_indicator_rollup,
    build_outcome_section,
    build_variance_table,
)
from apps.mel.tracking.models import Activity, ActivityStatus

pytestmark = pytest.mark.django_db


def _seed_logframe_and_indicator(slug="build-lf"):
    lf = LogFrame.objects.create(name=f"LF {slug}", slug=slug)
    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=f"ind-{slug}",
        name="Indicator",
        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 lf, output, ind


def test_build_indicator_rollup_aggregates_rag_counts():
    lf, _, ind = _seed_logframe_and_indicator(slug="rollup-1")
    record_data_point(
        indicator=ind,
        value=90,
        period_label="2026-04",
        auto_verify=True,
        source_module="m",
        source_event="e",
        source_object_id="1",
    )
    out = build_indicator_rollup(logframe=lf, period_label="2026-04")

    assert out["period_label"] == "2026-04"
    assert out["counts"].get("green") == 1
    assert len(out["rows"]) == 1
    row = out["rows"][0]
    assert row["code"] == ind.code
    assert row["actual"] == 90
    assert row["rag"] == "green"


def test_build_variance_table_returns_recorded_snapshots():
    from apps.mel.indicators.services import compute_variance

    lf, _, ind = _seed_logframe_and_indicator(slug="variance-1")
    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", cause_analysis="slow intake")

    out = build_variance_table(logframe=lf, period_label="2026-04")

    assert len(out["rows"]) == 1
    assert out["rows"][0]["code"] == ind.code
    assert out["rows"][0]["cause_analysis"] == "slow intake"


def test_build_activity_summary_filters_to_period():
    lf, output_row, _ = _seed_logframe_and_indicator(slug="act-1")
    act_row = LogFrameRow.objects.create(
        logframe=lf, level=LogFrameLevel.ACTIVITY, title="A", parent=output_row,
    )
    Activity.objects.create(
        logframe_row=act_row,
        name="In-period",
        scheduled_start=date(2026, 4, 2),
        scheduled_end=date(2026, 4, 25),
        status=ActivityStatus.IN_PROGRESS,
    )
    Activity.objects.create(
        logframe_row=act_row,
        name="Future",
        scheduled_start=date(2099, 1, 1),
        scheduled_end=date(2099, 1, 15),
        status=ActivityStatus.PLANNED,
    )

    out = build_activity_summary(
        logframe=lf, period_start=date(2026, 4, 1), period_end=date(2026, 4, 30),
    )

    assert len(out["rows"]) == 1
    assert out["rows"][0]["name"] == "In-period"
    assert out["counts"].get(ActivityStatus.IN_PROGRESS) == 1


def test_build_consolidated_data_runs_requested_sections():
    lf, _, ind = _seed_logframe_and_indicator(slug="consolidated-1")
    record_data_point(
        indicator=ind,
        value=30,
        period_label="2026-04",
        auto_verify=True,
        source_module="m",
        source_event="e",
        source_object_id="1",
    )

    out = build_consolidated_data(
        logframe=lf,
        period_label="2026-04",
        period_start=date(2026, 4, 1),
        period_end=date(2026, 4, 30),
        sections=["indicator_rollup", "compliance"],
    )

    assert "indicator_rollup" in out
    assert "compliance" in out
    assert "activity_summary" not in out  # not requested
    assert out["scope"]["logframe_slug"] == "consolidated-1"


def test_build_outcome_section_attaches_indicator_progress():
    lf = LogFrame.objects.create(name="LF outcome-sec", slug="outcome-sec")
    impact = LogFrameRow.objects.create(logframe=lf, level=LogFrameLevel.IMPACT, title="I")
    outcome = LogFrameRow.objects.create(
        logframe=lf, level=LogFrameLevel.OUTCOME, title="Adoption", parent=impact,
    )
    ind = Indicator.objects.create(
        logframe_row=outcome,
        code="oa-build-ind",
        name="Adoption rate",
        unit="%",
        calculation_method="latest",
        data_source="survey",
        frequency=IndicatorFrequency.QUARTERLY,
    )
    IndicatorTarget.objects.create(
        indicator=ind,
        period_label="2026-Q2",
        period_start=date(2026, 4, 1),
        period_end=date(2026, 6, 30),
        baseline_value=Decimal("0"),
        target_value=Decimal("100"),
    )
    record_data_point(
        indicator=ind,
        value=72,
        period_label="2026-Q2",
        auto_verify=True,
        source_module="survey",
        source_event="rollup",
        source_object_id="1",
    )
    OutcomeAssessment.objects.create(
        logframe_row=outcome,
        period_label="2026-Q2",
        contribution_analysis="Adoption rising.",
        recommendations="Sustain delivery cadence.",
        metrics={"reach": 320},
    )

    out = build_outcome_section(logframe=lf, period_label="2026-Q2")
    assert len(out["rows"]) == 1
    row = out["rows"][0]
    assert row["outcome_row"] == "Adoption"
    assert row["metrics"] == {"reach": 320}
    assert row["recommendations"] == "Sustain delivery cadence."
    assert len(row["indicators"]) == 1
    assert row["indicators"][0]["code"] == "oa-build-ind"
    assert row["indicators"][0]["actual"] == 72


def test_build_impact_section_orders_newest_first():
    lf = LogFrame.objects.create(name="LF impact-sec", slug="impact-sec")
    older = ImpactEvaluation.objects.create(
        logframe=lf,
        title="Older eval",
        completed_on=date(2024, 6, 30),
    )
    newer = ImpactEvaluation.objects.create(
        logframe=lf,
        title="Newer eval",
        completed_on=date(2026, 1, 15),
    )
    out = build_impact_section(logframe=lf)
    assert [r["title"] for r in out["rows"]] == ["Newer eval", "Older eval"]


def test_build_consolidated_data_includes_outcome_and_impact_when_requested():
    lf = LogFrame.objects.create(name="LF cons-eval", slug="cons-eval")
    impact = LogFrameRow.objects.create(logframe=lf, level=LogFrameLevel.IMPACT, title="I")
    outcome = LogFrameRow.objects.create(
        logframe=lf, level=LogFrameLevel.OUTCOME, title="O", parent=impact,
    )
    OutcomeAssessment.objects.create(
        logframe_row=outcome,
        period_label="2026-Q2",
        contribution_analysis="X",
    )
    ImpactEvaluation.objects.create(logframe=lf, title="Annual review")

    out = build_consolidated_data(
        logframe=lf,
        period_label="2026-Q2",
        period_start=date(2026, 4, 1),
        period_end=date(2026, 6, 30),
        sections=["outcome_assessment", "impact_evaluation"],
    )
    assert "outcome_assessment" in out
    assert "impact_evaluation" in out
    assert len(out["outcome_assessment"]["rows"]) == 1
    assert out["impact_evaluation"]["rows"][0]["title"] == "Annual review"
