from fxhnt.application.allocation_engine import target_capital
from fxhnt.application.allocation_policy import AllocationPolicy


def test_ceiling_binds_and_caps_gross():
    pol = AllocationPolicy(capital_ceiling=10_000.0)
    # weights sum to 1.0 (gross), equity 50k → implied gross 50k > ceiling 10k → scaled to 10k
    w = {"a": 0.5, "b": 0.5}
    d = target_capital(w, equity=50_000.0, policy=pol)
    assert abs(sum(abs(x) for x in d.values()) - 10_000.0) < 1e-6      # gross capped at ceiling
    assert abs(d["a"] - d["b"]) < 1e-6                                 # equal split preserved


def test_ceiling_not_binding_uses_equity():
    pol = AllocationPolicy(capital_ceiling=100_000.0)
    d = target_capital({"a": 0.5, "b": 0.5}, equity=20_000.0, policy=pol)
    assert abs(sum(abs(x) for x in d.values()) - 20_000.0) < 1e-6      # gross = equity·1.0 < ceiling


def test_empty_or_zero_weights():
    pol = AllocationPolicy(capital_ceiling=10_000.0)
    assert target_capital({}, 50_000.0, pol) == {}
    assert target_capital({"a": 0.0, "b": 0.0}, 50_000.0, pol) == {"a": 0.0, "b": 0.0}


def test_negative_weights_yield_negative_dollars_same_scale():
    pol = AllocationPolicy(capital_ceiling=10_000.0)
    d = target_capital({"long": 0.5, "short": -0.5}, equity=50_000.0, policy=pol)
    assert d["short"] < 0 < d["long"]                                   # sign preserved
    assert abs(abs(d["short"]) - abs(d["long"])) < 1e-6                 # same scaled magnitude
    assert abs(sum(abs(x) for x in d.values()) - 10_000.0) < 1e-6       # still capped at ceiling


def test_non_finite_equity_fails_safe_to_zero():
    import math
    pol = AllocationPolicy(capital_ceiling=10_000.0)
    d = target_capital({"a": 0.5, "b": 0.5}, equity=math.nan, policy=pol)
    assert d == {"a": 0.0, "b": 0.0}                                    # NaN equity → all-zero, not NaN
