"""Detecta la distribucion de idiomas de un corpus de textos.

Funcion pura y defensiva: el computo es determinista y local (sin I/O de red).
La libreria opcional `langdetect` se importa de forma perezosa dentro de la
funcion; si no esta instalada (o cualquier paso falla), la funcion degrada
limpiamente a `available=False` y NUNCA lanza excepciones.
"""


def detect_corpus_language(texts, top_k=10, sample_max=1000) -> dict:
    """Estima la distribucion de idiomas de un corpus con `langdetect`.

    Args:
        texts: lista de strings (documentos). Los elementos None, no-str o
            vacios tras strip se descartan.
        top_k: numero maximo de idiomas a devolver en `distribution`,
            ordenados por frecuencia descendente.
        sample_max: numero maximo de documentos a clasificar (se toman los
            primeros) para acotar el coste.

    Returns:
        dict con la forma fija (dict-no-throw):
        {
            "available": bool,   # True si langdetect es importable
            "n_detected": int,   # documentos clasificados con exito
            "distribution": [{"lang": str, "count": int, "pct": float}, ...],
            "dominant": str | None,
        }
    """
    degraded = {
        "available": False,
        "n_detected": 0,
        "distribution": [],
        "dominant": None,
    }
    try:
        # Import perezoso con degradacion: si langdetect no esta disponible,
        # devolvemos el dict degradado sin lanzar.
        try:
            from langdetect import detect, DetectorFactory

            # Semilla fija -> deteccion determinista entre ejecuciones.
            DetectorFactory.seed = 0
        except Exception:
            return dict(degraded)

        # Normaliza y filtra el corpus.
        docs = []
        if texts:
            for t in texts:
                if isinstance(t, str):
                    s = t.strip()
                    if s:
                        docs.append(s)

        # Muestreo de los primeros `sample_max` documentos.
        if sample_max is not None and sample_max >= 0:
            docs = docs[:sample_max]

        # Conteo por idioma; langdetect lanza LangDetectException en textos
        # sin features detectables -> se ignora y se sigue.
        counts: dict = {}
        for doc in docs:
            try:
                lang = detect(doc)
            except Exception:
                continue
            counts[lang] = counts.get(lang, 0) + 1

        n_detected = sum(counts.values())

        # Orden estable: por count descendente, desempate por codigo de idioma.
        ordered = sorted(counts.items(), key=lambda kv: (-kv[1], kv[0]))

        k = top_k if (top_k is not None and top_k >= 0) else len(ordered)
        distribution = []
        for lang, count in ordered[:k]:
            pct = round(count / n_detected * 100, 2) if n_detected else 0.0
            distribution.append({"lang": lang, "count": count, "pct": pct})

        dominant = ordered[0][0] if ordered else None

        return {
            "available": True,
            "n_detected": n_detected,
            "distribution": distribution,
            "dominant": dominant,
        }
    except Exception:
        # Cualquier fallo global degrada a available False sin lanzar.
        return dict(degraded)
