import math

from scoring import engine


def _complete_metrics():
    return {
        "fear_greed": {"value": 10, "classification": "Extreme Fear"},
        "puell_multiple": {"value": 0.3},
        "mvrv_zscore": {"value": -0.1},
        "drawdown": {"value": 60.0, "ath": 250.0},
        "price": {"price": 100.0},
        "200w_sma": {"value": 120.0},
        "reserve_risk": {"value": 0.001},
        "rhodl_ratio": {"value": 50.0},
        "nupl": {"value": -0.1},
        "lth_realized_price": {"value": 120.0},
        "hash_ribbons": {"buy_signal": False},
    }


def test_score_all_ml_normalizes_displayed_weights_and_contributions(monkeypatch):
    monkeypatch.setattr(
        engine,
        "load_ml_weights",
        lambda: {
            "fear_greed": 0.40,
            "puell_multiple": 0.20,
            "mvrv_zscore": 0.15,
            "drawdown": 0.10,
            "pct_above_200w_sma": 0.05,
            "reserve_risk": 0.04,
            "rhodl_ratio": 0.03,
            "nupl": 0.02,
            "pct_above_lth_rp": 0.01,
        },
    )

    scored = engine.score_all_ml(_complete_metrics())

    assert scored["ml_mode"] is True
    valid_metrics = [m for m in scored["metrics"] if m["score"] is not None]
    assert scored["ml_weight_total"] == 1.01  # trained weights + small hash-ribbons fallback
    assert math.isclose(sum(m["ml_weight"] for m in valid_metrics), 1.0, abs_tol=0.001)
    assert math.isclose(
        sum(m["ml_contribution"] for m in valid_metrics),
        scored["composite_score"],
        abs_tol=0.05,
    )

    hash_ribbons = next(m for m in valid_metrics if m["key"] == "hash_ribbons")
    assert hash_ribbons["ml_raw_weight"] == 0.01
    assert hash_ribbons["ml_weight"] == round(0.01 / 1.01, 4)


def test_score_all_ml_preserves_classic_fallback_when_weights_missing(monkeypatch):
    monkeypatch.setattr(engine, "load_ml_weights", lambda: {})

    scored = engine.score_all_ml(_complete_metrics())

    assert scored["ml_mode"] is False
    assert scored["ml_error"]
    assert scored["ml_artifact"]["valid"] is False
    assert "classic_score" not in scored
