"""Tests for microstructure invariance module."""

import pytest
from horizon._horizon import (
    InvarianceResult,
    trading_activity,
    invariant_bet_size,
    kyle_obizhaeva_impact,
    is_informed_trade,
    invariance_analysis,
)


class TestTradingActivity:
    def test_basic(self):
        w = trading_activity(1_000_000.0, 0.02)
        assert w > 0.0

    def test_higher_volume_higher_activity(self):
        w1 = trading_activity(1_000_000.0, 0.02)
        w2 = trading_activity(2_000_000.0, 0.02)
        assert w2 > w1

    def test_higher_vol_higher_activity(self):
        w1 = trading_activity(1_000_000.0, 0.01)
        w2 = trading_activity(1_000_000.0, 0.05)
        assert w2 > w1

    def test_zero_inputs(self):
        assert trading_activity(0.0, 0.02) == 0.0
        assert trading_activity(1_000_000.0, 0.0) == 0.0

    def test_negative_inputs(self):
        assert trading_activity(-1.0, 0.02) == 0.0
        assert trading_activity(1_000_000.0, -0.02) == 0.0

    def test_nan_inputs(self):
        assert trading_activity(float("nan"), 0.02) == 0.0
        assert trading_activity(1_000_000.0, float("nan")) == 0.0

    def test_inf_inputs(self):
        assert trading_activity(float("inf"), 0.02) == 0.0


class TestInvariantBetSize:
    def test_basic(self):
        bs = invariant_bet_size(1_000_000.0, 0.02, 1000)
        assert bs > 0.0

    def test_more_trades_smaller_bets(self):
        bs1 = invariant_bet_size(1_000_000.0, 0.02, 100)
        bs2 = invariant_bet_size(1_000_000.0, 0.02, 1000)
        assert bs2 < bs1

    def test_zero_trades(self):
        assert invariant_bet_size(1_000_000.0, 0.02, 0) == 0.0


class TestKyleObizhaevaImpact:
    def test_basic(self):
        impact = kyle_obizhaeva_impact(10000.0, 1_000_000.0, 0.02)
        assert impact > 0.0

    def test_larger_trades_more_impact(self):
        i1 = kyle_obizhaeva_impact(1000.0, 1_000_000.0, 0.02)
        i2 = kyle_obizhaeva_impact(10000.0, 1_000_000.0, 0.02)
        assert i2 > i1

    def test_zero_trade(self):
        assert kyle_obizhaeva_impact(0.0, 1_000_000.0, 0.02) == 0.0


class TestIsInformedTrade:
    def test_small_trade_not_informed(self):
        assert not is_informed_trade(100.0, 1_000_000.0, 0.02)

    def test_custom_threshold(self):
        result = is_informed_trade(100.0, 1_000_000.0, 0.02, 0.001)
        # With very low threshold, more trades are "informed"
        assert isinstance(result, bool)

    def test_zero_inputs(self):
        assert not is_informed_trade(0.0, 1_000_000.0, 0.02)
        assert not is_informed_trade(100.0, 0.0, 0.02)


class TestInvarianceAnalysis:
    def test_basic(self):
        result = invariance_analysis(1_000_000.0, 0.02, 1000, 1000.0)
        assert isinstance(result, InvarianceResult)
        assert result.trading_activity > 0.0
        assert result.bet_size > 0.0
        assert result.normal_trade_size > 0.0
        assert result.impact_per_bet > 0.0
        assert 0.0 <= result.market_efficiency_score <= 1.0

    def test_repr(self):
        result = invariance_analysis(1_000_000.0, 0.02, 1000, 1000.0)
        r = repr(result)
        assert "InvarianceResult" in r

    def test_invalid_inputs(self):
        with pytest.raises(ValueError):
            invariance_analysis(0.0, 0.02, 1000, 1000.0)
        with pytest.raises(ValueError):
            invariance_analysis(1_000_000.0, 0.02, 0, 1000.0)
        with pytest.raises(ValueError):
            invariance_analysis(1_000_000.0, 0.02, 1000, 0.0)

    def test_nan_inputs(self):
        with pytest.raises(ValueError):
            invariance_analysis(float("nan"), 0.02, 1000, 1000.0)
