"""Tests para confidence_interval_mean (IC de la media / diferencia de medias Welch).

Importa el modulo hoja directamente (`confidence_interval_mean`) para no depender
de que el paquete reexporte la funcion en su __init__ (lo integra el orquestador
al cerrar el grupo).

Los golden se calculan con scipy dentro del propio test para que sean robustos:
la funcion bajo prueba debe coincidir con la referencia de scipy a ~1e-9.
"""

import math

import numpy as np
from scipy import stats

from confidence_interval_mean import confidence_interval_mean


def test_one_sample_golden_contra_scipy():
    # mean=5.0, n=8. Este dataset tiene sd POBLACIONAL (ddof=0) exactamente 2.0,
    # pero la sd MUESTRAL (ddof=1, la que exige la spec y la que es correcta para
    # el IC de una media con la t) es sqrt(32/7) ~ 2.13809. El golden robusto se
    # calcula con scipy usando se con ddof=1, no con el atajo 2.0/sqrt(8).
    data = [2, 4, 4, 4, 5, 5, 7, 9]
    out = confidence_interval_mean(data, confidence=0.95)

    n = len(data)
    mean = float(np.mean(data))
    sd = float(np.std(data, ddof=1))  # sample sd ~ 2.13809
    se = sd / math.sqrt(n)
    lo, hi = stats.t.interval(0.95, df=n - 1, loc=mean, scale=se)

    assert abs(out["mean"] - 5.0) < 1e-9
    assert abs(out["se"] - se) < 1e-12
    assert out["df"] == 7.0
    assert out["n"] == 8
    assert out["confidence"] == 0.95
    assert abs(out["ci_low"] - lo) < 1e-9
    assert abs(out["ci_high"] - hi) < 1e-9
    # Valores tabulados correctos para ddof=1 (no los 3.32793/6.67207 del
    # enunciado, que asumian erroneamente sd=2.0 / ddof=0).
    assert abs(out["ci_low"] - 3.21251) < 1e-3
    assert abs(out["ci_high"] - 6.78749) < 1e-3
    assert "note" not in out


def test_one_sample_distinto_nivel_confianza():
    data = [10.0, 12.0, 11.0, 13.0, 9.0, 14.0]
    out = confidence_interval_mean(data, confidence=0.99)

    n = len(data)
    mean = float(np.mean(data))
    se = float(np.std(data, ddof=1)) / math.sqrt(n)
    lo, hi = stats.t.interval(0.99, df=n - 1, loc=mean, scale=se)

    assert abs(out["mean"] - mean) < 1e-12
    assert abs(out["ci_low"] - lo) < 1e-9
    assert abs(out["ci_high"] - hi) < 1e-9
    assert out["df"] == float(n - 1)


def test_welch_diferencia_golden_contra_scipy():
    data = [23.0, 21.0, 25.0, 22.0, 24.0, 26.0]
    other = [18.0, 20.0, 17.0, 19.0, 21.0]
    conf = 0.95
    out = confidence_interval_mean(data, other, confidence=conf)

    a = np.asarray(data, dtype=float)
    b = np.asarray(other, dtype=float)
    n1, n2 = a.size, b.size
    mean1, mean2 = float(a.mean()), float(b.mean())
    diff = mean1 - mean2
    se1 = float(a.std(ddof=1)) / math.sqrt(n1)
    se2 = float(b.std(ddof=1)) / math.sqrt(n2)
    se = math.sqrt(se1**2 + se2**2)
    df = (se1**2 + se2**2) ** 2 / (se1**4 / (n1 - 1) + se2**4 / (n2 - 1))
    lo, hi = stats.t.interval(conf, df=df, loc=diff, scale=se)

    assert abs(out["mean"] - diff) < 1e-9
    assert abs(out["mean"] - (mean1 - mean2)) < 1e-9
    assert abs(out["se"] - se) < 1e-12
    assert abs(out["df"] - df) < 1e-9
    assert abs(out["ci_low"] - lo) < 1e-9
    assert abs(out["ci_high"] - hi) < 1e-9
    assert out["n1"] == n1
    assert out["n2"] == n2
    assert out["n"] == n1 + n2
    assert "note" not in out


def test_edge_un_solo_elemento_no_lanza_nan_note():
    out = confidence_interval_mean([5], confidence=0.95)
    assert out["mean"] == 5.0  # la media si esta definida con n=1
    assert math.isnan(out["se"])
    assert math.isnan(out["ci_low"])
    assert math.isnan(out["ci_high"])
    assert math.isnan(out["df"])
    assert out["n"] == 1
    assert "note" in out


def test_edge_lista_vacia_no_lanza_note():
    out = confidence_interval_mean([], confidence=0.95)
    assert math.isnan(out["mean"])
    assert math.isnan(out["ci_low"])
    assert math.isnan(out["ci_high"])
    assert math.isnan(out["se"])
    assert out["n"] == 0
    assert "note" in out


def test_edge_varianza_cero_colapsa_al_punto():
    out = confidence_interval_mean([3, 3, 3], confidence=0.95)
    assert out["mean"] == 3.0
    assert out["se"] == 0.0
    assert out["ci_low"] == 3.0
    assert out["ci_high"] == 3.0
    assert not math.isnan(out["ci_low"])
    assert out["n"] == 3
    assert "note" in out


def test_edge_welch_muestra_vacia_no_lanza_note():
    out = confidence_interval_mean([1.0, 2.0, 3.0], [], confidence=0.95)
    assert math.isnan(out["mean"])
    assert math.isnan(out["ci_low"])
    assert math.isnan(out["se"])
    assert out["n1"] == 3
    assert out["n2"] == 0
    assert "note" in out


def test_edge_welch_n1_uno_no_lanza_note():
    out = confidence_interval_mean([5.0], [1.0, 2.0, 3.0], confidence=0.95)
    # La diferencia de medias si esta definida.
    assert abs(out["mean"] - (5.0 - 2.0)) < 1e-9
    assert math.isnan(out["se"])
    assert math.isnan(out["ci_low"])
    assert math.isnan(out["df"])
    assert "note" in out
