"""Détient un `LocalePipeline` par locale supportée — le seul état mutable
partagé du process (une instance vit pour toute la durée de vie d'`uvicorn`,
montée sur `app.state`, voir `main.py`).

Volontairement une classe "registre" séparée de `LocalePipeline` lui-même :
`LocalePipeline` ne connaît qu'une seule locale, ce module route `process`
vers la bonne instance selon le `locale` reçu dans la requête — même
séparation de responsabilité que `TechStepClassifierService` (une seule
instance, un seul `NlpManager` multi-langues) avait implicitement via
node-nlp, explicitée ici puisque spaCy charge un modèle par langue.
"""

import logging

from .locale_pipeline import SUPPORTED_LOCALES, LocalePipeline, ProcessResult, TrainEntry
from .training_data import entries_for_locale

logger = logging.getLogger(__name__)


class PipelineRegistry:
    def __init__(self) -> None:
        self._pipelines: dict[str, LocalePipeline] = {
            locale: LocalePipeline(locale) for locale in SUPPORTED_LOCALES
        }

    def initialize(self) -> None:
        """Charge le modèle spaCy de base *et* entraîne chaque locale connue
        depuis `training_data.TECH_STEP_TRAINING_DATA` — appelé une fois au
        démarrage du process (`main.py`'s `lifespan`), avant que `uvicorn`
        n'accepte de requêtes.

        Contrairement à la version précédente de ce service (où `apps/api`
        poussait le corpus via `POST /v1/train` à son propre warm-up), ce
        service est maintenant entièrement autonome : `apps/api` ne connaît
        plus aucune technique, seulement le résultat de
        `POST /v1/process`. `GET /health` ne répond `200` qu'une fois cette
        méthode terminée (chargement *et* entraînement) — pas seulement le
        chargement — pour que `docker-compose.yml`'s `depends_on: ...
        condition: service_healthy` (et la boucle d'attente équivalente en
        CI) ne laisse jamais `apps/api` démarrer face à un service qui
        répondrait mais ne saurait encore rien détecter.
        """
        logger.info("tech-step NLP initializing pipelines", extra={"locales": list(self._pipelines)})
        for locale, pipeline in self._pipelines.items():
            pipeline.preload()
            entries = [TrainEntry(**entry) for entry in entries_for_locale(locale)]
            label_count, example_count, synonym_count = pipeline.train(entries)
            logger.info(
                "tech-step NLP pipeline trained",
                extra={
                    "locale": locale,
                    "labelCount": label_count,
                    # Nombre réel d'exemples donnés au textcat (utterances
                    # *et* synonyms combinés — voir `LocalePipeline.train`),
                    # pas seulement le compte d'`utterances` du corpus.
                    "exampleCount": example_count,
                    "synonymCount": synonym_count,
                },
            )
        logger.info("tech-step NLP pipelines ready", extra={"locales": list(self._pipelines)})

    def process(self, locale: str, text: str) -> ProcessResult:
        pipeline = self._pipelines.get(locale)
        if pipeline is None:
            # Une locale que ce service ne sait structurellement pas
            # charger (pas de modèle spaCy connu) se comporte comme une
            # locale "jamais entraînée" côté `process` — reproduit le test
            # `apps/api` existant ("returns an empty sequence for a locale
            # nothing was trained on"), qui ne distingue pas les deux cas.
            return ProcessResult(entities=[], intent=None, score=0.0)
        return pipeline.process(text)


registry = PipelineRegistry()
