"""G8 — Indicator forecasting (linear regression projection)."""
from __future__ import annotations

from datetime import datetime, timedelta, timezone as dt_tz
from decimal import Decimal

import pytest
from django.utils import timezone

from apps.mel.indicators.forecasting import (
    DEFAULT_HORIZON,
    MIN_HISTORY_POINTS,
    build_forecast_chart,
    forecast_indicator,
)
from apps.mel.indicators.models import (
    DataPoint,
    Indicator,
    IndicatorFrequency,
    LogFrame,
    LogFrameLevel,
    LogFrameRow,
)


pytestmark = pytest.mark.django_db


def _make_indicator(code: str, frequency=IndicatorFrequency.MONTHLY):
    lf = LogFrame.objects.create(name=f"LF {code}", slug=f"lf-{code}")
    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)
    return Indicator.objects.create(
        logframe_row=output, code=code, name=code,
        unit="count", calculation_method="count", data_source="manual",
        frequency=frequency,
    )


def _seed_history(indicator, values: list[float]):
    base = timezone.now() - timedelta(days=30 * len(values))
    for i, v in enumerate(values):
        DataPoint.objects.create(
            indicator=indicator,
            value=Decimal(str(v)),
            period_label=f"2025-{i+1:02d}",
            reported_at=base + timedelta(days=30 * i),
            status=DataPoint.Status.VERIFIED,
        )


def test_forecast_returns_none_when_insufficient_history():
    ind = _make_indicator("g8-a")
    _seed_history(ind, [10.0, 20.0])  # only 2 points
    result = forecast_indicator(ind)
    assert result.has_data is False
    assert result.history_count == 2
    assert result.points == []


def test_forecast_with_linear_trend_projects_upward():
    ind = _make_indicator("g8-b")
    _seed_history(ind, [10.0, 20.0, 30.0, 40.0, 50.0, 60.0])
    result = forecast_indicator(ind, periods=4)
    assert result.has_data is True
    assert len(result.points) == 4
    # Slope should be positive and forecast values monotonically increasing.
    assert result.slope > 0
    values = [p.value for p in result.points]
    assert values == sorted(values)
    # Confidence band widens as we project further out.
    margins = [p.upper - p.value for p in result.points]
    assert margins == sorted(margins)


def test_build_forecast_chart_emits_svg_payload():
    ind = _make_indicator("g8-c")
    _seed_history(ind, [5.0, 10.0, 15.0, 20.0, 25.0])
    result = forecast_indicator(ind)
    chart = build_forecast_chart(result)
    assert chart["has_data"] is True
    assert chart["history_points"]
    assert chart["forecast_points"]
    assert chart["band_polygon"]
    assert chart["horizon"] == DEFAULT_HORIZON
    assert chart["min_history"] == MIN_HISTORY_POINTS


def test_forecast_chart_empty_state_carries_history_count():
    ind = _make_indicator("g8-d")
    _seed_history(ind, [1.0, 2.0])
    result = forecast_indicator(ind)
    chart = build_forecast_chart(result)
    assert chart == {
        "has_data": False,
        "history_count": 2,
        "min_history": MIN_HISTORY_POINTS,
    }
