"""Tests for Benjamini-Hochberg adjustment across sections."""

from __future__ import annotations

import math

import numpy as np
import pytest

from ablate.stats.multiplicity import DEFAULT_FDR_Q, benjamini_hochberg


def test_adjusted_values_are_never_smaller_than_raw():
    raw = [0.001, 0.01, 0.03, 0.2, 0.5]
    result = benjamini_hochberg(raw)
    assert all(a >= p for a, p in zip(result.adjusted, raw, strict=True))


def test_adjustment_is_monotone_in_the_raw_order():
    """A section must never show a smaller adjusted p than one ranked above it."""
    raw = [0.001, 0.02, 0.03, 0.04, 0.05, 0.9]
    adjusted = benjamini_hochberg(raw).adjusted
    assert list(adjusted) == sorted(adjusted)


def test_adjusted_values_are_capped_at_one():
    assert all(a <= 1.0 for a in benjamini_hochberg([0.5, 0.8, 0.99]).adjusted)


def test_order_is_preserved_for_the_caller():
    """Returned in input order, not sorted order — the report indexes by
    section, and a silent reordering would mislabel every row."""
    result = benjamini_hochberg([0.5, 0.001, 0.2])
    assert result.adjusted[1] < result.adjusted[2] < result.adjusted[0]


def test_a_clear_signal_survives_adjustment():
    result = benjamini_hochberg([0.0001] + [0.9] * 11)
    assert result.rejected[0]
    assert result.n_discoveries == 1


def test_uniform_noise_yields_no_discoveries():
    """The 46% spurious-finding rate at K=12 is what this exists to stop."""
    rng = np.random.default_rng(0)
    spurious = 0
    for _ in range(200):
        if benjamini_hochberg(rng.uniform(0, 1, 12)).n_discoveries:
            spurious += 1
    assert spurious / 200 <= 0.15  # FDR-controlled, versus ~0.46 unadjusted


def test_fdr_is_less_strict_than_bonferroni():
    """Bonferroni at K=15 would leave nothing detectable at any affordable N."""
    raw = [0.004] * 15
    bh = benjamini_hochberg(raw, q=0.10)
    bonferroni_rejects = [p * len(raw) <= 0.05 for p in raw]
    assert bh.n_discoveries > sum(bonferroni_rejects)


def test_q_threshold_is_respected():
    raw = [0.02, 0.03, 0.9, 0.95]
    assert benjamini_hochberg(raw, q=0.01).n_discoveries == 0
    assert benjamini_hochberg(raw, q=0.50).n_discoveries >= 2


def test_default_q_matches_the_documented_screening_level():
    assert DEFAULT_FDR_Q == 0.10


def test_withheld_p_values_stay_nan_and_never_count_as_discoveries():
    """A section whose interval was withheld has no p to adjust. It must not
    be silently promoted, nor silently treated as a null result."""
    result = benjamini_hochberg([0.001, float("nan"), 0.9])
    assert math.isnan(result.adjusted[1])
    assert not result.rejected[1]
    assert result.rejected[0]


def test_all_nan_input_is_handled():
    result = benjamini_hochberg([float("nan")] * 3)
    assert result.n_discoveries == 0
    assert all(math.isnan(a) for a in result.adjusted)


def test_empty_input_is_handled():
    assert benjamini_hochberg([]).n_discoveries == 0


def test_single_comparison_needs_no_adjustment():
    assert benjamini_hochberg([0.04]).adjusted[0] == pytest.approx(0.04)
