import math
from fxhnt.application.paper_book import isv_adaptive_weights

def _series(vals, start=1):   # {epoch_day: ret}
    return {start + i: v for i, v in enumerate(vals)}

def test_empty_active_returns_empty():
    assert isv_adaptive_weights({}, []) == {}

def test_no_history_is_equal_weight():
    w = isv_adaptive_weights({"a": {}, "b": {}}, ["a", "b"])
    assert w == {"a": 0.5, "b": 0.5}

def test_low_vol_sleeve_gets_higher_weight():
    lo = _series([0.001, -0.001] * 30)    # ~60 obs low vol
    hi = _series([0.05, -0.05] * 30)      # ~60 obs high vol
    w = isv_adaptive_weights({"lo": lo, "hi": hi}, ["lo", "hi"])
    assert math.isclose(sum(w.values()), 1.0, abs_tol=1e-9)
    assert w["lo"] > w["hi"]

def test_rising_vol_regime_shrinks_lookback():
    # calm must extend through the PRIOR block (indices 40-49); only the last 10 (indices 50-59) are hot,
    # so std(recent L_min=10) > std(prior L_min=10). Expose L_t via the debug return for determinism.
    calm = [0.01, -0.01] * 25            # 50 obs low vol (covers the prior block)
    hot = [0.06, -0.06] * 5              # last 10 obs high vol (the recent block)
    s = _series(calm + hot)
    _, dbg = isv_adaptive_weights({"x": s, "y": _series([0.01, -0.01] * 30)},
                                  ["x", "y"], _debug=True)
    assert dbg["L_t"] < 60
    flat = _series([0.01, -0.01] * 40)
    _, dbg2 = isv_adaptive_weights({"x": flat, "y": flat}, ["x", "y"], _debug=True)
    assert dbg2["L_t"] == 60

def test_cap_never_below_one_over_n():
    s = _series([0.02, -0.02] * 30)
    _, dbg = isv_adaptive_weights({"a": s, "b": s, "c": s}, ["a", "b", "c"], _debug=True)
    assert dbg["cap_t"] >= 1.0 / 3 - 1e-9

def test_floor_holds_after_waterfill():
    # one very-low-vol sleeve would dominate; floor keeps the other above phi/N.
    dom = _series([0.0001, -0.0001] * 40)
    weak = _series([0.2, -0.2] * 40)
    w = isv_adaptive_weights({"dom": dom, "weak": weak}, ["dom", "weak"])
    assert math.isclose(sum(w.values()), 1.0, abs_tol=1e-9)
    assert w["weak"] >= 0.5 / 2 - 1e-9          # w_floor = phi/N = 0.25
    assert w["dom"] <= 1.0 + 1e-9

def test_mixed_history_cold_sleeve_gets_floor():
    has = _series([0.02, -0.02] * 30)
    w = isv_adaptive_weights({"has": has, "cold": {1: 0.01}}, ["has", "cold"])   # cold has <2 obs
    assert math.isclose(w["cold"], 0.5 / 2, abs_tol=1e-9)                        # exactly w_floor
    assert math.isclose(sum(w.values()), 1.0, abs_tol=1e-9)
