"""Deteccion de tendencia en una serie via regresion lineal simple (grupo eda)."""

from __future__ import annotations

import math

from scipy.stats import linregress


def trend_slope(values: list, x: list = None) -> dict:
    """Detecta la tendencia (sube/baja/plana) de una serie y su significancia.

    Ajusta una regresion lineal simple (minimos cuadrados) de ``values`` sobre
    ``x`` y resume el resultado en una direccion legible mas estadisticos. Si
    ``x`` es ``None`` se usa el indice posicional ``0..n-1``. Los pares cuyo
    valor (en ``values`` o ``x``) sea ``None`` o ``NaN`` se descartan antes del
    ajuste, de modo que series con huecos se manejan sin fallar.

    Funcion pura y determinista: no hace I/O, no muta los inputs.

    Args:
        values: serie de valores numericos (la variable dependiente, eje Y).
        x: posiciones de cada valor (la variable independiente, eje X). Si es
            ``None`` se usa ``range(len(values))``. Debe tener la misma longitud
            que ``values`` cuando se proporciona.

    Returns:
        dict con la pendiente y el resumen de la tendencia:

        - ``slope``: pendiente de la recta ajustada (float) o ``None`` si no
          hay suficientes pares validos.
        - ``intercept``: ordenada en el origen (float).
        - ``r``: coeficiente de correlacion de Pearson (float).
        - ``r_squared``: ``r**2``, fraccion de varianza explicada (float).
        - ``p_value``: p-valor del test de pendiente nula (float).
        - ``std_err``: error estandar de la pendiente (float).
        - ``direction``: ``"up"`` (slope > 0 y significativa), ``"down"``
          (slope < 0 y significativa), ``"flat"`` (no significativa) o
          ``"unknown"`` (menos de 3 pares validos).
        - ``significant``: ``True`` si ``p_value < 0.05``.
        - ``n``: numero de pares validos usados en el ajuste.

        Con menos de 3 pares validos devuelve
        ``{"slope": None, "direction": "unknown", "significant": False,
        "n": <n>}``.
    """
    xs_raw = list(range(len(values))) if x is None else list(x)
    ys_raw = list(values)

    xs: list[float] = []
    ys: list[float] = []
    for xi, yi in zip(xs_raw, ys_raw):
        if xi is None or yi is None:
            continue
        if isinstance(xi, float) and math.isnan(xi):
            continue
        if isinstance(yi, float) and math.isnan(yi):
            continue
        xs.append(float(xi))
        ys.append(float(yi))

    n = len(xs)
    if n < 3:
        return {"slope": None, "direction": "unknown", "significant": False, "n": n}

    result = linregress(xs, ys)
    slope = float(result.slope)
    p_value = float(result.pvalue)
    r = float(result.rvalue)

    significant = p_value < 0.05
    if not significant:
        direction = "flat"
    elif slope > 0:
        direction = "up"
    elif slope < 0:
        direction = "down"
    else:
        direction = "flat"

    return {
        "slope": slope,
        "intercept": float(result.intercept),
        "r": r,
        "r_squared": r * r,
        "p_value": p_value,
        "std_err": float(result.stderr),
        "direction": direction,
        "significant": significant,
        "n": n,
    }
