"""Registre des adaptateurs de source (spec Imports §5).

Les noms d'adaptateurs sont **la** provenance : ils atterrissent dans `keyword_metrics.source`
et dans `keyword_lists.source`, et c'est sous ce nom que l'utilisateur voit la valeur, la filtre
au réservoir et l'isole en `?view=<nom>`. Le registre est leur seule déclaration : depuis la
suppression de la hiérarchie de sources (spec 2026-09-01 §3.1), la consolidation SQL ne cite
plus aucune source nommément — une source qu'elle ne connaît pas n'est plus traitée autrement
qu'une autre, et l'arrimage aux ensembles `KNOWN_SOURCES` / `CONSOLIDATION_SOURCES` /
`FALLBACK_SOURCES` de `app/keywords/models.py` a disparu avec eux.

Contrepartie assumée : plus rien n'échoue si un adaptateur est nommé de travers — la faute de
frappe crée une source fantôme. Le jour où un adaptateur s'ajoute (T7/T8 DataForSEO), le
verrou honnête est un test qui fige `names()`.

La détection confronte l'en-tête du fichier aux signatures (§5) ; ambiguïté ou aucune
correspondance → `generic_csv` et son mapping manuel, jamais un nom de fichier.
"""

from collections.abc import Sequence

from app.keywords.sources.base import Adapter, Detection
from app.keywords.sources.generic import GenericCsv, MappingError
from app.keywords.sources.mangools import MangoolsKwfinder
from app.keywords.sources.semrush import SemrushMagic, SemrushPositions
from app.keywords.sources.suggest import Suggestion, suggest_columns

GENERIC = GenericCsv()

#  **Granularité des noms de source, actée 2026-09-01.** Semrush en a deux
#  (`semrush_positions`, `semrush_magic`) parce que ses deux exports n'ont pas les mêmes
#  capacités : l'un observe un classement (URL + position), l'autre non — la provenance doit
#  dire lequel a produit la valeur. Mangools n'en a **qu'un**, au niveau du fournisseur : le
#  CSV KWFinder et l'API KWFinder (T8, BYOK) rendent les mêmes métriques sur les mêmes
#  mots-clés, seul le tuyau change. Les distinguer découperait la consolidation en deux pour
#  une différence qui n'apparaît nulle part à l'écran — et coûterait une migration de données
#  le jour où on voudrait les recoller. L'adaptateur API T8 réutilisera donc `mangools`.
_ADAPTERS: dict[str, Adapter] = {
    adapter.name: adapter
    for adapter in (SemrushPositions(), SemrushMagic(), MangoolsKwfinder(), GENERIC)
}


def get(name: str) -> Adapter | None:
    return _ADAPTERS.get(name)


def names() -> list[str]:
    return sorted(_ADAPTERS)


def detect(headers: Sequence[str]) -> list[Detection]:
    """Les candidatures pour cet en-tête, la meilleure d'abord (étape 2 du wizard).

    Liste vide = aucune signature ne reconnaît le fichier : c'est `generic_csv` et son
    mapping manuel qu'il faut proposer. Deux candidatures de confiance égale = ambiguïté,
    même conclusion — on ne tranche jamais tout seul, et jamais sur un nom de fichier.
    """
    detections = [
        detection
        for adapter in _ADAPTERS.values()
        if (detection := adapter.detect(headers)) is not None
    ]
    return sorted(detections, key=lambda detection: detection.confidence, reverse=True)


def best(headers: Sequence[str]) -> Detection | None:
    """La candidature retenue, ou `None` si aucune ou si deux se valent (ambiguïté)."""
    detections = detect(headers)
    if not detections:
        return None
    if len(detections) > 1 and detections[0].confidence == detections[1].confidence:
        return None
    return detections[0]


__all__ = [
    "GENERIC",
    "Adapter",
    "Detection",
    "MappingError",
    "Suggestion",
    "best",
    "detect",
    "get",
    "names",
    "suggest_columns",
]
