"""
services/agent_service.py
=========================
Pipeline complet de traitement d'une question utilisateur :
    valider_input → classifier → react / conversation / hors_scope
    → trace Langfuse → score LLM-as-Judge → mémoire → monitoring
"""

from __future__ import annotations

import logging
import time
from dataclasses import dataclass

import domain.agent.memory as memory
import domain.core.langfuse_http as langfuse_http
import domain.core.monitoring as monitoring
import domain.core.scorer as scorer
from domain.agent.react import HORS_SCOPE, _afficher_resultat, react_loop
from domain.core.llm import appeler_llm, classifier_question, get_usage_courant, reset_usage
from domain.core.security import InputSecurityError, valider_input

logger = logging.getLogger(__name__)


@dataclass
class ReponseAgent:
    reponse:  str
    mode:     str
    duree_ms: float


def traiter_question(question: str) -> ReponseAgent:
    """
    Exécute le pipeline complet pour une question utilisateur.

    Raises:
        InputSecurityError: injection ou contenu dangereux détecté.
        ValueError: erreur fonctionnelle LLM (question mal formée, etc.).
        RuntimeError: erreur d'infrastructure (réseau, token, etc.).
    """
    debut  = time.perf_counter()
    mode   = "inconnu"
    erreur: str | None = None

    reset_usage()
    logger.debug("Début traitement | longueur=%d", len(question))

    # Validation sécurité — InputSecurityError sort immédiatement (pas de monitoring)
    valider_input(question)
    logger.debug("Validation sécurité OK")

    trace_id = langfuse_http.create_trace(name="api-question", input=question)
    logger.debug("Trace Langfuse créée : %s", trace_id)

    try:
        historique_long  = memory.recall(4)
        historique_court = memory.recall(3)

        mode = classifier_question(question, historique_long)
        logger.info("Mode détecté : %s | question=%r", mode, question[:80])

        if mode == "hors_scope":
            reponse = HORS_SCOPE
            logger.info("Hors scope — réponse statique retournée")

        elif mode == "conversation":
            reponse = appeler_llm(question, historique=historique_court)
            logger.info("Conversation — réponse générée (longueur=%d car.)", len(reponse))

        else:
            logger.debug("Analyse — démarrage boucle ReAct")
            resultat = react_loop(question, historique_court)
            reponse  = _afficher_resultat(resultat)
            logger.info("Analyse — ReAct terminé | longueur_réponse=%d", len(reponse))

        langfuse_http.update_trace(trace_id, output=reponse, metadata={"mode": mode})

        if mode != "hors_scope":
            scorer.noter_async(trace_id, question, reponse)

        memory.store({"role": "user",      "content": question})
        memory.store({"role": "assistant",  "content": reponse})

    except (ValueError, RuntimeError) as exc:
        erreur = str(exc)
        logger.error(
            "Erreur fonctionnelle | mode=%s | question=%r | erreur=%s",
            mode, question[:80], exc,
            exc_info=True,
        )
        raise

    except Exception as exc:
        erreur = str(exc)
        logger.error(
            "Erreur inattendue | mode=%s | type=%s | question=%r | erreur=%s",
            mode, type(exc).__name__, question[:80], exc,
            exc_info=True,
        )
        raise RuntimeError(f"Erreur interne : {exc}") from exc

    finally:
        duree_ms = (time.perf_counter() - debut) * 1000
        usage    = get_usage_courant()
        monitoring.enregistrer(
            question=question,
            duree_ms=duree_ms,
            tokens_prompt=usage["prompt_tokens"],
            tokens_completion=usage["completion_tokens"],
            erreur=erreur,
        )
        logger.debug(
            "Pipeline terminé | mode=%s | durée=%.0f ms | tokens_in=%d | tokens_out=%d",
            mode, duree_ms, usage["prompt_tokens"], usage["completion_tokens"],
        )

    return ReponseAgent(reponse=reponse, mode=mode, duree_ms=round(duree_ms, 1))
