"""Golden tests for backend/services/coverage_classifier.py.

`classify_coverage` is the canonical, LT-relative inventory-health rule. These
tests pin every branch and boundary so that when the other pages are migrated
onto it (roadmap: "one source of truth"), any behavior change is caught.

Rule (from the module docstring):
    effective_cover = (stock_now + incoming_in_lt) / weekly_demand
    eff <= LT          -> CRITICAL
    LT < eff <= 1.5*LT -> ORDER_SOON
    1.5*LT < eff <= 2*LT -> HEALTHY
    eff > 2*LT         -> OVERSTOCK
    LT missing/<=0     -> MISSING_LT
    demand <= 0        -> NO_DEMAND
"""
import pytest

from backend.services.coverage_classifier import (
    classify_coverage,
    incoming_within_lt,
)


# LT=2 → soon threshold 3.0, overstock threshold 4.0. demand=10 so cover = stock/10.
@pytest.mark.parametrize("stock, expected", [
    (15, "CRITICAL"),     # eff 1.5  <= 2
    (25, "ORDER_SOON"),   # eff 2.5  in (2, 3]
    (35, "HEALTHY"),      # eff 3.5  in (3, 4]
    (50, "OVERSTOCK"),    # eff 5.0  > 4
])
def test_status_buckets(stock, expected):
    r = classify_coverage(stock_now=stock, weekly_demand=10.0, lead_time_weeks=2.0)
    assert r.status == expected


@pytest.mark.parametrize("stock, expected", [
    (20, "CRITICAL"),    # eff == LT (2.0) -> CRITICAL (inclusive lower edge)
    (30, "ORDER_SOON"),  # eff == 1.5*LT (3.0) -> ORDER_SOON
    (40, "HEALTHY"),     # eff == 2*LT (4.0) -> HEALTHY
])
def test_boundary_values_are_inclusive_on_the_upper_edge(stock, expected):
    r = classify_coverage(stock_now=stock, weekly_demand=10.0, lead_time_weeks=2.0)
    assert r.status == expected


def test_incoming_in_lt_shifts_the_bucket():
    # Without incoming this is CRITICAL (eff 1.5); 10 units arriving in-LT push
    # eff to 2.5 → ORDER_SOON.
    crit = classify_coverage(stock_now=15, weekly_demand=10.0, lead_time_weeks=2.0)
    assert crit.status == "CRITICAL"
    soon = classify_coverage(stock_now=15, weekly_demand=10.0, lead_time_weeks=2.0,
                             incoming_in_lt=10.0)
    assert soon.status == "ORDER_SOON"
    # bare cover ignores incoming; effective cover includes it.
    assert soon.weeks_cover == pytest.approx(1.5)
    assert soon.effective_weeks_cover == pytest.approx(2.5)


@pytest.mark.parametrize("lt", [None, 0, -1])
def test_missing_lead_time(lt):
    r = classify_coverage(stock_now=100, weekly_demand=10.0, lead_time_weeks=lt)
    assert r.status == "MISSING_LT"
    assert r.weeks_cover is None
    assert r.effective_weeks_cover is None
    assert r.lead_time_weeks is None
    assert r.is_at_risk is False and r.is_overstock is False and r.is_healthy is False


def test_no_demand():
    r = classify_coverage(stock_now=100, weekly_demand=0.0, lead_time_weeks=2.0)
    assert r.status == "NO_DEMAND"
    assert r.weeks_cover is None
    # thresholds are still reported even when demand is zero
    assert r.soon_threshold == pytest.approx(3.0)
    assert r.overstock_threshold == pytest.approx(4.0)
    assert r.is_at_risk is False


def test_flags_match_status():
    crit = classify_coverage(stock_now=15, weekly_demand=10.0, lead_time_weeks=2.0)
    soon = classify_coverage(stock_now=25, weekly_demand=10.0, lead_time_weeks=2.0)
    heal = classify_coverage(stock_now=35, weekly_demand=10.0, lead_time_weeks=2.0)
    over = classify_coverage(stock_now=50, weekly_demand=10.0, lead_time_weeks=2.0)
    assert crit.is_at_risk and soon.is_at_risk
    assert not heal.is_at_risk and not over.is_at_risk
    assert heal.is_healthy and not over.is_healthy
    assert over.is_overstock and not heal.is_overstock


# ------------------------------------------------------------------
# incoming_within_lt — sum incoming over the LT window, prorating the
# trailing partial week.
# ------------------------------------------------------------------

_HORIZON = [(2026, 1), (2026, 2), (2026, 3), (2026, 4)]
_INC = {
    (5, 2026, 1): 10.0,
    (5, 2026, 2): 20.0,
    (5, 2026, 3): 30.0,
    (5, 2026, 4): 40.0,
}


def test_incoming_within_lt_whole_weeks():
    # LT=2 → first two weeks: 10 + 20.
    assert incoming_within_lt(_HORIZON, _INC, pid=5, lead_time_weeks=2.0) == pytest.approx(30.0)


def test_incoming_within_lt_prorates_partial_trailing_week():
    # LT=2.5 → 10 + 20 + 0.5*30.
    assert incoming_within_lt(_HORIZON, _INC, pid=5, lead_time_weeks=2.5) == pytest.approx(45.0)


def test_incoming_within_lt_zero_or_empty():
    assert incoming_within_lt(_HORIZON, _INC, pid=5, lead_time_weeks=0) == 0.0
    assert incoming_within_lt([], _INC, pid=5, lead_time_weeks=2.0) == 0.0
    # unknown pid → nothing matches
    assert incoming_within_lt(_HORIZON, _INC, pid=999, lead_time_weeks=2.0) == 0.0
