"""Tests for adaptive N calculation."""


from autodebug.detection.adaptive_n import (
    calculate_adaptive_n,
    detection_power,
    estimate_flake_rate_from_history,
)


class TestCalculateAdaptiveN:
    """Test adaptive N calculation."""

    def test_high_flake_rate_needs_few_runs(self):
        """50% flake rate should need very few runs."""
        n = calculate_adaptive_n(0.50, target_power=0.95)
        assert n <= 10

    def test_low_flake_rate_needs_many_runs(self):
        """10% flake rate should need ~29 runs."""
        n = calculate_adaptive_n(0.10, target_power=0.95)
        assert 25 <= n <= 35

    def test_very_low_flake_rate_capped(self):
        """1% flake rate would need 299 runs, but max_n caps it."""
        n = calculate_adaptive_n(0.01, target_power=0.95, max_n=100)
        assert n == 100

    def test_respects_min_n(self):
        """Never returns less than min_n."""
        n = calculate_adaptive_n(0.99, target_power=0.95, min_n=5)
        assert n >= 5

    def test_respects_max_n(self):
        """Never returns more than max_n."""
        n = calculate_adaptive_n(0.01, target_power=0.95, max_n=50)
        assert n <= 50

    def test_budget_constraint(self):
        """Time budget should cap N."""
        # 60 second budget, 5 seconds per test -> max 12 runs
        n = calculate_adaptive_n(
            0.10,
            target_power=0.95,
            budget_seconds=60,
            avg_test_duration_seconds=5,
        )
        assert n <= 12

    def test_zero_flake_rate_returns_min(self):
        """Zero flake rate should return min_n."""
        n = calculate_adaptive_n(0.0, target_power=0.95, min_n=5)
        assert n == 5

    def test_invalid_power_returns_min(self):
        """Invalid power should return min_n."""
        n = calculate_adaptive_n(0.10, target_power=0.0, min_n=5)
        assert n == 5

    def test_default_parameters(self):
        """Default parameters should produce reasonable N."""
        n = calculate_adaptive_n(0.10)
        assert 5 <= n <= 100


class TestDetectionPower:
    """Test detection power calculation."""

    def test_high_n_high_power(self):
        """Many runs with moderate flake rate should give high power."""
        power = detection_power(0.10, 30)
        assert power >= 0.95

    def test_low_n_low_power(self):
        """Few runs with low flake rate should give low power."""
        power = detection_power(0.05, 5)
        assert power < 0.30

    def test_zero_rate_zero_power(self):
        power = detection_power(0.0, 100)
        assert power == 0.0

    def test_zero_n_zero_power(self):
        power = detection_power(0.50, 0)
        assert power == 0.0

    def test_perfect_rate(self):
        """100% flake rate should always be detected (invalid edge case)."""
        power = detection_power(1.0, 1)
        assert power == 0.0  # Edge case: rate >= 1 returns 0


class TestEstimateFlakeRate:
    """Test flake rate estimation from history."""

    def test_insufficient_data_returns_default(self):
        rate = estimate_flake_rate_from_history(2, 1)
        assert rate == 0.10

    def test_no_failures(self):
        rate = estimate_flake_rate_from_history(100, 0)
        assert rate == 0.01  # Clamped to minimum

    def test_half_failures(self):
        rate = estimate_flake_rate_from_history(100, 50)
        assert rate == 0.50

    def test_clamped_maximum(self):
        rate = estimate_flake_rate_from_history(10, 8)
        assert rate <= 0.50
