"""Property-based tests for risk tier default parameters.

Feature: autonomous-trading-engine

Validates that the three risk tier defaults (conservative, moderate, aggressive)
have valid parameter ranges and correct ordering relationships.

**Validates: Requirements 5.1**
"""
from __future__ import annotations

from hypothesis import given, settings
from hypothesis import strategies as st

from services.trading.models import RISK_TIER_DEFAULTS, RiskTierConfig

# ---------------------------------------------------------------------------
# Hypothesis strategies
# ---------------------------------------------------------------------------

def _risk_tier_config_strategy() -> st.SearchStrategy[RiskTierConfig]:
    """Generate random RiskTierConfig objects with valid parameter ranges."""
    return st.builds(
        RiskTierConfig,
        name=st.sampled_from(["conservative", "moderate", "aggressive"]),
        min_confidence=st.floats(min_value=0.0, max_value=1.0, allow_nan=False),
        max_position_pct=st.floats(
            min_value=0.01, max_value=1.0, allow_nan=False
        ),
        stop_loss_atr_multiplier=st.floats(
            min_value=0.01, max_value=10.0, allow_nan=False
        ),
        reward_risk_ratio=st.floats(
            min_value=0.01, max_value=10.0, allow_nan=False
        ),
        max_sector_pct=st.floats(
            min_value=0.01, max_value=1.0, allow_nan=False
        ),
        max_portfolio_heat=st.floats(
            min_value=0.01, max_value=1.0, allow_nan=False
        ),
    )


# ---------------------------------------------------------------------------
# Property 29 (partial): Risk tier default parameter validation
# ---------------------------------------------------------------------------


EXPECTED_TIERS = {"conservative", "moderate", "aggressive"}


def test_all_three_tiers_exist() -> None:
    """All three risk tiers must be present in RISK_TIER_DEFAULTS.

    **Validates: Requirements 5.1**
    """
    assert set(RISK_TIER_DEFAULTS.keys()) == EXPECTED_TIERS


def test_each_tier_has_valid_parameter_ranges() -> None:
    """Each tier's parameters must fall within valid ranges.

    **Validates: Requirements 5.1**
    """
    for tier_name, cfg in RISK_TIER_DEFAULTS.items():
        assert cfg.name == tier_name, (
            f"Tier name mismatch: key={tier_name}, cfg.name={cfg.name}"
        )
        # min_confidence in [0, 1]
        assert 0.0 <= cfg.min_confidence <= 1.0, (
            f"{tier_name}: min_confidence={cfg.min_confidence} not in [0, 1]"
        )
        # max_position_pct in (0, 1]
        assert 0.0 < cfg.max_position_pct <= 1.0, (
            f"{tier_name}: max_position_pct={cfg.max_position_pct} not in (0, 1]"
        )
        # stop_loss_atr_multiplier > 0
        assert cfg.stop_loss_atr_multiplier > 0.0, (
            f"{tier_name}: stop_loss_atr_multiplier={cfg.stop_loss_atr_multiplier} not > 0"
        )
        # reward_risk_ratio > 0
        assert cfg.reward_risk_ratio > 0.0, (
            f"{tier_name}: reward_risk_ratio={cfg.reward_risk_ratio} not > 0"
        )
        # max_sector_pct in (0, 1]
        assert 0.0 < cfg.max_sector_pct <= 1.0, (
            f"{tier_name}: max_sector_pct={cfg.max_sector_pct} not in (0, 1]"
        )
        # max_portfolio_heat in (0, 1]
        assert 0.0 < cfg.max_portfolio_heat <= 1.0, (
            f"{tier_name}: max_portfolio_heat={cfg.max_portfolio_heat} not in (0, 1]"
        )


def test_min_confidence_ordering() -> None:
    """Conservative has highest min_confidence, aggressive has lowest.

    conservative.min_confidence > moderate.min_confidence > aggressive.min_confidence

    **Validates: Requirements 5.1**
    """
    c = RISK_TIER_DEFAULTS["conservative"]
    m = RISK_TIER_DEFAULTS["moderate"]
    a = RISK_TIER_DEFAULTS["aggressive"]

    assert c.min_confidence > m.min_confidence > a.min_confidence, (
        f"min_confidence ordering violated: "
        f"conservative={c.min_confidence}, "
        f"moderate={m.min_confidence}, "
        f"aggressive={a.min_confidence}"
    )


def test_max_position_pct_ordering() -> None:
    """Conservative has lowest max_position_pct, aggressive has highest.

    conservative.max_position_pct < moderate.max_position_pct < aggressive.max_position_pct

    **Validates: Requirements 5.1**
    """
    c = RISK_TIER_DEFAULTS["conservative"]
    m = RISK_TIER_DEFAULTS["moderate"]
    a = RISK_TIER_DEFAULTS["aggressive"]

    assert c.max_position_pct < m.max_position_pct < a.max_position_pct, (
        f"max_position_pct ordering violated: "
        f"conservative={c.max_position_pct}, "
        f"moderate={m.max_position_pct}, "
        f"aggressive={a.max_position_pct}"
    )


def test_max_portfolio_heat_ordering() -> None:
    """Conservative has lowest max_portfolio_heat, aggressive has highest.

    conservative.max_portfolio_heat < moderate.max_portfolio_heat < aggressive.max_portfolio_heat

    **Validates: Requirements 5.1**
    """
    c = RISK_TIER_DEFAULTS["conservative"]
    m = RISK_TIER_DEFAULTS["moderate"]
    a = RISK_TIER_DEFAULTS["aggressive"]

    assert c.max_portfolio_heat < m.max_portfolio_heat < a.max_portfolio_heat, (
        f"max_portfolio_heat ordering violated: "
        f"conservative={c.max_portfolio_heat}, "
        f"moderate={m.max_portfolio_heat}, "
        f"aggressive={a.max_portfolio_heat}"
    )


@settings(max_examples=100)
@given(cfg=_risk_tier_config_strategy())
def test_random_risk_tier_config_parameter_ranges(cfg: RiskTierConfig) -> None:
    """Any randomly generated RiskTierConfig with valid inputs satisfies range invariants.

    This property test verifies that the parameter range constraints hold for
    arbitrary RiskTierConfig instances, not just the defaults.

    **Validates: Requirements 5.1**
    """
    assert 0.0 <= cfg.min_confidence <= 1.0
    assert 0.0 < cfg.max_position_pct <= 1.0
    assert cfg.stop_loss_atr_multiplier > 0.0
    assert cfg.reward_risk_ratio > 0.0
    assert 0.0 < cfg.max_sector_pct <= 1.0
    assert 0.0 < cfg.max_portfolio_heat <= 1.0
