"""Couche 'capabilities moteurs' : vérité testable sur les moteurs disponibles.

Un moteur n'est *disponible* que si ses dépendances (et son modèle, pour
CamemBERT) chargent réellement dans l'environnement courant. Cette couche est
consommée par la GUI (afficher/désactiver) et le CLI (`--engines`) pour que
l'application ne promette jamais un moteur qu'elle n'embarque pas.

Sondes injectables → aucun modèle, aucun réseau dans les tests.
"""

from __future__ import annotations

import engine_capabilities as ec


def _probes(camembert=True, eds=False, gliner=False):
    return {
        "camembert": lambda: (camembert, "ok" if camembert else "modèle absent"),
        "eds": lambda: (eds, "ok" if eds else "edsnlp non embarqué"),
        "gliner": lambda: (gliner, "ok" if gliner else "gliner non embarqué"),
    }


def test_capabilities_map_reads_injected_probes():
    caps = ec.capabilities_map(probes=_probes(camembert=True, eds=False, gliner=False))
    assert set(caps) == {"camembert", "eds", "gliner"}
    assert caps["camembert"].available is True
    assert caps["eds"].available is False
    assert caps["gliner"].available is False


def test_camembert_required_others_optional():
    caps = ec.capabilities_map(probes=_probes())
    assert caps["camembert"].required is True
    assert caps["eds"].required is False
    assert caps["gliner"].required is False


def test_reason_surfaced_when_unavailable():
    caps = ec.capabilities_map(probes=_probes(eds=False))
    assert "edsnlp" in caps["eds"].reason  # explication présentable à l'utilisateur
    # un moteur disponible expose aussi une raison non vide
    assert caps["camembert"].reason


def test_available_engines_filters_unavailable():
    avail = ec.available_engines(probes=_probes(camembert=True, eds=True, gliner=False))
    keys = {c.key for c in avail}
    assert keys == {"camembert", "eds"}


def test_labels_are_human_readable():
    caps = ec.capabilities_map(probes=_probes())
    assert "CamemBERT" in caps["camembert"].label
    assert "EDS" in caps["eds"].label
    assert "GLiNER" in caps["gliner"].label


def test_default_probes_run_without_crash_and_are_consistent():
    """Les sondes par défaut (find_spec + fichier modèle) ne crashent pas et
    renvoient un booléen + une raison non vide pour chaque moteur."""
    caps = ec.capabilities_map()  # sondes réelles de l'environnement
    assert set(caps) == {"camembert", "eds", "gliner"}
    for cap in caps.values():
        assert isinstance(cap.available, bool)
        assert isinstance(cap.reason, str) and cap.reason
