"""Numerical parity tests for merge_p_values against the R reference values."""
import numpy as np
import pandas as pd
import pytest

from activepathways import merge_p_values


# Same matrix used by the R test suite (filled column-wise from c(0.01, 0.06, 0.8, 0.0001, 0, 1)).
TEST_MATRIX = np.array([[0.01, 0.0001], [0.06, 0.0], [0.8, 1.0]])
TEST_VECTOR = np.array([0.01, 0.06, 0.8, 0.0001, 0.0, 1.0])


def test_fisher_matrix_matches_r():
    expected = np.array([1.481551e-05, 4.167534e-299, 9.785148e-01])
    np.testing.assert_allclose(merge_p_values(TEST_MATRIX, "Fisher"), expected, atol=1e-7, rtol=1e-5)


def test_brown_matrix_matches_r():
    expected = np.array([2.52747e-05, 0.0, 9.73873e-01])
    np.testing.assert_allclose(merge_p_values(TEST_MATRIX, "Brown"), expected, atol=1e-7, rtol=1e-5)


def test_fisher_vector_matches_r():
    assert merge_p_values(TEST_VECTOR, "Fisher") == pytest.approx(7.147579e-296, rel=1e-5)


def test_single_column_matrix_returns_input_column():
    out = merge_p_values(TEST_MATRIX[:, [0]], "Fisher")
    np.testing.assert_array_equal(np.asarray(out), TEST_MATRIX[:, 0])


def test_pvalue_floor_clamping():
    inflated = np.array([1.0, 1e-400])
    threshold = np.array([1.0, 1e-300])
    assert merge_p_values(inflated) == pytest.approx(merge_p_values(threshold))


def test_pvalue_floor_clamping_matrix():
    inflated = np.array([[1.0, 1.0], [1e-320, 1e-310]])
    threshold = np.array([[1.0, 1.0], [1e-300, 1e-300]])
    np.testing.assert_allclose(merge_p_values(inflated), merge_p_values(threshold))


def test_directional_symmetry():
    # Flipping both the score directions and the constraints must produce the same result.
    a = merge_p_values([0.05, 0.01], "Fisher_directional", [1, -1], [-1, 1])
    b = merge_p_values([0.05, 0.01], "Fisher_directional", [1, -1], [1, -1])
    assert a == pytest.approx(b)


def test_invalid_pvalues_raise():
    with pytest.raises(ValueError, match="missing values"):
        merge_p_values([0.5, np.nan])
    with pytest.raises(ValueError, match=r"\[0,1\]"):
        merge_p_values([-0.1, 0.5])
    with pytest.raises(ValueError, match=r"\[0,1\]"):
        merge_p_values([0.5, 1.1])


def test_brown_rejects_vector_input():
    with pytest.raises(ValueError, match="single list"):
        merge_p_values([0.05, 0.10], "Brown")


def test_unknown_method_raises():
    with pytest.raises(ValueError, match="currently supported"):
        merge_p_values([0.05, 0.10], "Tippett")


def test_direction_required_for_directional_methods():
    with pytest.raises(ValueError, match="must be provided for directional"):
        merge_p_values([0.05, 0.10], "Fisher_directional")


def test_dataframe_input_returns_named_series(adenocarcinoma_scores):
    out = merge_p_values(adenocarcinoma_scores, "Brown")
    assert isinstance(out, pd.Series)
    # Index of merged Series should be the row labels of the input frame.
    assert list(out.index) == list(adenocarcinoma_scores.index)
    assert out.shape[0] == adenocarcinoma_scores.shape[0]
