"""Golden tests for the pure scenario math in
backend/services/scenario_service.py.

These feed supplier-facing cancel/postpone/produce recommendations, so the
thresholds and the weeks-of-cover walk need to stay stable.
"""
import pytest

from backend.services.scenario_service import (
    classify,
    real_weeks_cover,
    week_series,
    week_step,
)


# ------------------------------------------------------------------
# week_step / week_series — naive 52-week wrap (matches app.py)
# ------------------------------------------------------------------

def test_week_step_wraps_forward_past_52():
    assert week_step(2026, 52, 1) == (2027, 1)
    assert week_step(2026, 10, 5) == (2026, 15)


def test_week_step_wraps_backward_below_1():
    assert week_step(2026, 1, -1) == (2025, 52)


def test_week_series_crosses_year_boundary():
    assert week_series(2026, 51, 3) == [(2026, 51), (2026, 52), (2027, 1)]


# ------------------------------------------------------------------
# real_weeks_cover — fractional walk-forward
# ------------------------------------------------------------------

def test_real_weeks_cover_partial_last_week():
    # 25 units, 10/wk: full week, full week, then 5/10 of the third.
    assert real_weeks_cover(25.0, [10.0, 10.0, 10.0]) == pytest.approx(2.5)


def test_real_weeks_cover_skips_zero_demand_weeks():
    # week 1 demand 0 counts as a full covered week; week 2 partial.
    assert real_weeks_cover(5.0, [0.0, 10.0]) == pytest.approx(1.5)


def test_real_weeks_cover_extrapolates_with_trailing_average():
    # Stock outlasts the series; extrapolate the remainder at the avg (10/wk).
    # 30 units over [10,10] leaves 10 after 2 weeks → +10/10 = 3.0 weeks.
    assert real_weeks_cover(30.0, [10.0, 10.0]) == pytest.approx(3.0)


def test_real_weeks_cover_zero_total_demand_returns_huge():
    # No demand anywhere → effectively infinite cover (horizon + 999).
    assert real_weeks_cover(5.0, [0.0, 0.0]) == pytest.approx(1001.0)


# ------------------------------------------------------------------
# classify — 3-state CANCEL / POSTPONE / PRODUCE
# ------------------------------------------------------------------

def test_classify_cancel_when_cover_now_meets_threshold():
    assert classify(10.0, 12.0, cancel_threshold=8.0) == "CANCEL"


def test_classify_postpone_below_cancel_but_above_trigger():
    assert classify(5.0, 6.0, cancel_threshold=8.0) == "POSTPONE"


def test_classify_produce_below_postpone_trigger():
    assert classify(2.0, 2.0, cancel_threshold=8.0) == "PRODUCE"


def test_classify_no_cancel_threshold_skips_cancel():
    # cancel_threshold None → can never CANCEL; 10 >= 4 → POSTPONE.
    assert classify(10.0, 10.0, cancel_threshold=None) == "POSTPONE"


@pytest.mark.parametrize("cover, expected", [
    (4.0, "POSTPONE"),   # >= default postpone_trigger (4.0)
    (3.99, "PRODUCE"),
])
def test_classify_postpone_trigger_boundary(cover, expected):
    assert classify(cover, cover, cancel_threshold=None) == expected
