"""Legibilidad Flesch Reading Ease de un corpus de texto.

Función pura del grupo `eda`, estilo dict-no-throw: nunca lanza. Usa la
librería `textstat` con import perezoso y degradación: si `textstat` no está
instalada (o falla al importar), devuelve un resultado con `available=False`
en lugar de propagar el error.
"""


def _percentile_nearest_rank(sorted_values, pct):
    """Percentil por nearest-rank sobre una lista ya ordenada ascendente.

    rank = ceil(pct/100 * n); índice 1-based recortado a [1, n].
    Devuelve None si la lista está vacía.
    """
    n = len(sorted_values)
    if n == 0:
        return None
    import math

    rank = math.ceil((pct / 100.0) * n)
    if rank < 1:
        rank = 1
    if rank > n:
        rank = n
    return sorted_values[rank - 1]


def compute_text_readability(texts, sample_max=500) -> dict:
    """Calcula la legibilidad Flesch Reading Ease de un corpus.

    Args:
        texts: lista de str. Los elementos None, no-str o vacíos (tras strip)
            se descartan. Se muestrean los primeros `sample_max` documentos
            válidos.
        sample_max: número máximo de documentos a puntuar (los primeros).

    Returns:
        Dict con la forma exacta::

            {"available": bool, "n_scored": int,
             "flesch": {"mean": float|None, "p50": float|None,
                        "min": float|None, "max": float|None}}

        `available` es True si `textstat` se pudo importar. La función nunca
        lanza: cualquier excepción global degrada a `available=False`.
    """
    empty = {
        "available": False,
        "n_scored": 0,
        "flesch": {"mean": None, "p50": None, "min": None, "max": None},
    }
    try:
        # Import perezoso con degradación: textstat es una dependencia opcional.
        try:
            import textstat
        except Exception:
            return {
                "available": False,
                "n_scored": 0,
                "flesch": {"mean": None, "p50": None, "min": None, "max": None},
            }

        # Filtrar y muestrear documentos válidos (los primeros sample_max).
        docs = []
        if texts is not None:
            try:
                limit = int(sample_max)
            except Exception:
                limit = 500
            if limit < 0:
                limit = 0
            for item in texts:
                if not isinstance(item, str):
                    continue
                if item.strip() == "":
                    continue
                docs.append(item)
                if len(docs) >= limit:
                    break

        scores = []
        for doc in docs:
            try:
                score = textstat.flesch_reading_ease(doc)
            except Exception:
                continue
            try:
                score = float(score)
            except Exception:
                continue
            scores.append(score)

        n_scored = len(scores)
        if n_scored == 0:
            # textstat presente pero corpus vacío / sin puntuar.
            return {
                "available": True,
                "n_scored": 0,
                "flesch": {"mean": None, "p50": None, "min": None, "max": None},
            }

        mean_val = round(sum(scores) / n_scored, 1)
        sorted_scores = sorted(scores)
        p50_raw = _percentile_nearest_rank(sorted_scores, 50)
        p50_val = round(p50_raw, 1) if p50_raw is not None else None
        min_val = sorted_scores[0]
        max_val = sorted_scores[-1]

        return {
            "available": True,
            "n_scored": n_scored,
            "flesch": {
                "mean": mean_val,
                "p50": p50_val,
                "min": min_val,
                "max": max_val,
            },
        }
    except Exception:
        return empty
