"""Tests para stl_decompose."""

import numpy as np

from stl_decompose import stl_decompose


def _serie_estacional(n: int, period: int, trend: float, amp: float, seed: int) -> list:
    rng = np.random.default_rng(seed)
    return [
        trend * i + amp * np.sin(2 * np.pi * i / period) + rng.normal(0, 1)
        for i in range(n)
    ]


def test_serie_con_tendencia_y_estacionalidad():
    serie = _serie_estacional(n=120, period=12, trend=0.3, amp=10.0, seed=0)
    res = stl_decompose(serie, period=12)
    assert res["period"] == 12
    assert res["trend_strength"] > 0.5
    assert res["seasonal_strength"] > 0.5
    assert len(res["trend"]["values"]) == 120


def test_fuerza_estacional_alta_con_estacionalidad_fuerte():
    # Amplitud estacional grande, ruido pequeno => seasonal_strength cercano a 1.
    serie = _serie_estacional(n=120, period=12, trend=0.05, amp=20.0, seed=1)
    res = stl_decompose(serie, period=12)
    assert res["seasonal_strength"] > 0.9


def test_infiere_periodo_si_none():
    serie = _serie_estacional(n=120, period=12, trend=0.1, amp=10.0, seed=2)
    res = stl_decompose(serie)  # period=None
    assert res.get("period_inferred") is True
    assert res["period"] is not None


def test_serie_corta_devuelve_nota():
    # period=12 pero solo 20 puntos (< 2*period=24): nota, no descompone.
    serie = _serie_estacional(n=20, period=12, trend=0.1, amp=5.0, seed=3)
    res = stl_decompose(serie, period=12)
    assert "note" in res
    assert res["trend_strength"] is None


def test_muestra_insuficiente_devuelve_nota():
    res = stl_decompose([1, 2, 3, 4, 5])
    assert res["n"] == 5
    assert res["note"] == "datos insuficientes"
    assert res["seasonal_strength"] is None


def test_descarta_none_y_nan():
    serie = _serie_estacional(n=120, period=12, trend=0.2, amp=8.0, seed=4)
    sucio = []
    for i, v in enumerate(serie):
        sucio.append(v)
        if i % 30 == 0:
            sucio.append(None)
            sucio.append(float("nan"))
    res = stl_decompose(sucio, period=12)
    assert res["n"] == 120


def test_serie_larga_resume_sin_values():
    # >200 puntos: las componentes vienen resumidas sin 'values'.
    serie = _serie_estacional(n=300, period=12, trend=0.1, amp=10.0, seed=5)
    res = stl_decompose(serie, period=12)
    assert res["trend"]["values"] is None
    assert "mean" in res["trend"]
    assert "note" in res["trend"]


# --- H2: deteccion de periodo robusta a tendencia (detrend) ------------------

def test_h2_infer_period_detrend_con_tendencia_fuerte():
    # Golden: serie con tendencia FUERTE + estacionalidad de periodo 12. Sin detrend
    # la autocorrelacion cruda decae monotonamente y el lag minimo (2) gana siempre
    # (period=2 espurio). Con el detrend lineal el lag ganador es el periodo real.
    from stl_decompose import _infer_period

    serie = _serie_estacional(n=120, period=12, trend=0.8, amp=10.0, seed=0)
    arr = np.asarray(serie, dtype=float)
    assert _infer_period(arr, max_period=60) == 12


def test_h2_auto_period_no_degenera_a_2():
    # End-to-end: stl_decompose(period=None) sobre serie con tendencia fuerte detecta
    # estacionalidad real en vez de reportar period=2 y seasonal_strength ~ 0
    # (el falso negativo de estacionalidad que motivo el fix H2).
    serie = _serie_estacional(n=120, period=12, trend=0.8, amp=10.0, seed=0)
    res = stl_decompose(serie)
    assert res["period"] != 2
    assert res["seasonal_strength"] > 0.5


def test_h2_serie_sin_estacionalidad_no_inventa_periodo():
    # Edge: serie con SOLO tendencia (sin componente estacional) no debe inventar un
    # periodo; tras el detrend el residuo es ruido sin pico de autocorrelacion claro.
    rng = np.random.default_rng(7)
    serie = [0.5 * i + rng.normal(0, 1) for i in range(120)]
    res = stl_decompose(serie)
    # Sin periodo fiable: nota explicita, nunca seasonal_strength=0 como conclusion.
    assert res["trend_strength"] is None
    assert "note" in res
