"""Tests for wholesale per-buyer one-off detection — encoded from real patterns
seen in the data (Konzum bulk order, dm ramp, tiny pharmacy buyers)."""
import numpy as np

from forecast_v4.cleaning_wholesale import detect_one_offs, strip_one_offs


def test_konzum_bulk_order_is_one_off():
    # Near-weekly buyer ~487/order with one 13,635 bulk week (their own promo).
    rng = [480, 500, 460, 520, 487, 510, 470, 495, 505, 465, 490, 500, 475, 515]
    qty = np.array(rng + [13635] + rng, dtype=float)
    mask = detect_one_offs(qty)
    assert mask[len(rng)], "the 13,635 bulk week should be flagged as a one-off"
    assert mask.sum() == 1, "only the bulk week should be flagged"
    cleaned, excess = strip_one_offs(qty)
    assert excess[len(rng)] > 12000          # the stripped one-off volume
    assert cleaned[len(rng)] < 600            # reduced to ~typical order size
    assert abs((cleaned + excess).sum() - qty.sum()) < 1e-6   # reconstructs


def test_dm_sustained_ramp_is_kept():
    # ~100/order for a while, then a sustained step up to ~400 (4+ orders) — a
    # genuine new baseline, NOT one-offs.
    qty = np.array([100, 110, 90, 105, 95, 100, 110, 95, 400, 410, 420, 405, 415],
                   dtype=float)
    mask = detect_one_offs(qty, min_run=3)
    assert mask.sum() == 0, f"sustained ramp wrongly flagged: {np.where(mask)[0]}"


def test_tiny_buyer_protected_by_floor():
    # Median order ~15; a 60-unit week is 4x its median but below the 300 floor.
    qty = np.array([10, 20, 15, 12, 18, 60, 14, 16, 22, 11], dtype=float)
    mask = detect_one_offs(qty, floor_units=300.0)
    assert mask.sum() == 0, "tiny-buyer order wrongly flagged (floor should protect)"


def test_regular_monthly_buyer_none_flagged():
    # Orders ~120 every 4th week, nothing else — pure rhythm, no one-offs.
    qty = np.zeros(40)
    qty[::4] = [120, 115, 125, 118, 122, 119, 121, 117, 123, 120]
    mask = detect_one_offs(qty)
    assert mask.sum() == 0


def test_sparse_buyer_not_judged():
    # Only 2 orders total — too few to tell one-off from rhythm; flag none.
    qty = np.zeros(30)
    qty[5] = 200
    qty[20] = 5000
    assert detect_one_offs(qty, min_orders=4).sum() == 0


def test_strip_reconstructs_when_no_oneoffs():
    qty = np.array([100, 110, 90, 105, 95, 100], dtype=float)
    cleaned, excess = strip_one_offs(qty)
    assert np.allclose(cleaned, qty)
    assert np.allclose(excess, 0.0)
