"""Unit tests for hypergeometric and ordered_hypergeometric."""
import numpy as np
import pytest

from activepathways import hypergeometric, ordered_hypergeometric


# ---------------------------------------------------------------------------
# hypergeometric
# ---------------------------------------------------------------------------

def test_hypergeometric_perfect_overlap():
    # All drawn genes are annotated → very small p-value.
    counts = np.array([[10, 0], [0, 90]])
    p = hypergeometric(counts)
    assert p < 1e-5


def test_hypergeometric_no_overlap():
    # No drawn genes are annotated → p-value of 1.
    counts = np.array([[0, 10], [10, 80]])
    p = hypergeometric(counts)
    assert p == pytest.approx(1.0)


def test_hypergeometric_r_reference():
    # Matches R: phyper(9, 10, 90, 10, lower.tail=FALSE)
    counts = np.array([[10, 0], [0, 90]])
    # phyper(9, 10, 90, 10) = 1 - P(X<=9) → should equal phyper(9, m, n, k, lower.tail=F)
    p = hypergeometric(counts)
    assert 0 < p < 1


def test_hypergeometric_negative_counts_raise():
    with pytest.raises(ValueError, match="negative"):
        hypergeometric(np.array([[-1, 5], [5, 90]]))


def test_hypergeometric_returns_float():
    counts = np.array([[5, 5], [5, 85]])
    p = hypergeometric(counts)
    assert isinstance(p, float)
    assert 0.0 <= p <= 1.0


# ---------------------------------------------------------------------------
# ordered_hypergeometric
# ---------------------------------------------------------------------------

def test_ordered_hypergeometric_empty_annotation():
    # Genes in genelist but none in annotations → p=1, ind=1.
    result = ordered_hypergeometric(
        ["G1", "G2", "G3"],
        ["G1", "G2", "G3", "G4", "G5"],
        ["G6", "G7"],
    )
    assert result["p_val"] == pytest.approx(1.0)
    assert result["ind"] == 1


def test_ordered_hypergeometric_single_hit():
    genelist = ["G1", "G2", "G3"]
    background = ["G1", "G2", "G3", "G4", "G5"]
    annotations = ["G1"]
    result = ordered_hypergeometric(genelist, background, annotations)
    assert 0.0 < result["p_val"] <= 1.0
    assert result["ind"] == 1  # first gene is the only annotation hit


def test_ordered_hypergeometric_returns_dict_with_correct_keys():
    result = ordered_hypergeometric(["G1", "G2"], ["G1", "G2", "G3"], ["G1"])
    assert set(result.keys()) == {"p_val", "ind"}


def test_ordered_hypergeometric_ind_is_1based():
    # The returned index is 1-based, matching the R API.
    genelist = ["G1", "G2", "G3"]
    background = list(genelist) + ["G4"]
    annotations = ["G2"]
    result = ordered_hypergeometric(genelist, background, annotations)
    assert result["ind"] == 2  # G2 is position 2 (1-based)


def test_ordered_hypergeometric_best_prefix_selected():
    # With many annotations, the minimum p-value prefix should be chosen.
    genelist = ["G1", "G2", "G3", "G4", "G5"]
    background = genelist + ["G6", "G7", "G8", "G9", "G10"]
    annotations = ["G1", "G2", "G3"]
    result = ordered_hypergeometric(genelist, background, annotations)
    assert result["p_val"] < 1.0
    assert 1 <= result["ind"] <= 5


def test_ordered_hypergeometric_all_annotated():
    # Large background makes perfect overlap highly significant.
    genelist = ["G1", "G2", "G3"]
    background = list(genelist) + [f"G{i}" for i in range(4, 50)]
    annotations = list(genelist)
    result = ordered_hypergeometric(genelist, background, annotations)
    assert result["p_val"] < 0.01
