"""Orchestration AI : route les requêtes des clients vers LiteLLM (s01).

LiteLLM (:4000) est OpenAI-compatible et route lui-même vers Qwen3 (g01) ou Claude
selon l'alias `hermes-default` / `hermes-switch`. L'intégration des outils Hermes
(« agir sur Funk » via le gateway :8080) est une étape ultérieure.
"""

from __future__ import annotations

import httpx

from stt_server.config import settings

# Client persistant (pooling + keep-alive) : évite un handshake TCP vers LiteLLM à chaque tour.
_client: httpx.AsyncClient | None = None


def _get_client() -> httpx.AsyncClient:
    global _client
    if _client is None or _client.is_closed:
        _client = httpx.AsyncClient(timeout=settings.request_timeout)
    return _client


async def aclose() -> None:
    global _client
    if _client is not None and not _client.is_closed:
        await _client.aclose()
    _client = None


async def ask(
    text: str,
    model: str | None = None,
    history: list[dict] | None = None,
    memories: list[str] | None = None,
    knowledge: list[str] | None = None,
) -> str:
    system = settings.system_prompt
    if settings.disable_thinking:
        # Qwen3 est un modèle « thinking » : sans ça il dépense tout le budget max_tokens
        # en raisonnement (`reasoning_content`) → `content` vide, ou part en réflexion longue
        # → timeout (502). Le token de contrôle `/no_think` désactive le mode raisonnement.
        # Inoffensif pour les modèles non-Qwen (simple texte ignoré).
        system += "\n/no_think"
    if knowledge:
        # Contexte AUTORITAIRE : extraits de la doc Funk (RAG funk-docs). Le modèle doit
        # répondre à partir de ça pour les questions sur le homelab (sinon il hallucine).
        extraits = "\n\n".join(knowledge)
        system += (
            "\n\n=== Documentation Funk (réponds À PARTIR de ces extraits ; "
            "si l'info n'y est pas, dis-le) ===\n" + extraits
        )
    if memories:
        souvenirs = "\n".join(f"- {m}" for m in memories)
        system += (
            "\n\nÉléments de mémoire long-terme (peuvent aider, ignore si hors-sujet) :\n"
            + souvenirs
        )
    messages = [{"role": "system", "content": system}]
    if history:
        messages += history
    messages.append({"role": "user", "content": text})
    payload = {
        "model": model or settings.model,
        "messages": messages,
        "max_tokens": settings.max_tokens,
        "temperature": settings.temperature,
    }
    headers = {"Authorization": f"Bearer {settings.litellm_key}"}
    # Résilience : la chaîne LiteLLM→llama-server (ROCm) se fige par à-coups. Une 2ᵉ tentative
    # récupère un blip transitoire (ex. connexion keep-alive périmée) au lieu de renvoyer 502.
    # Timeout PAR tentative (`request_timeout`) → on ne dépasse pas ~2× ce budget au pire.
    last_exc: Exception | None = None
    for attempt in range(2):
        try:
            r = await _get_client().post(
                settings.litellm_url,
                json=payload,
                headers=headers,
                timeout=settings.request_timeout,
            )
            r.raise_for_status()
            break
        except httpx.HTTPError as e:
            last_exc = e
            await aclose()  # repart sur une connexion neuve (pool potentiellement vicié)
    else:
        raise last_exc  # type: ignore[misc]
    msg = r.json()["choices"][0]["message"]
    # Filet de sécurité : si un modèle « thinking » renvoie un content vide (tout parti
    # en reasoning_content), on récupère le raisonnement plutôt que de renvoyer "".
    content = (msg.get("content") or "").strip()
    if not content:
        content = (msg.get("reasoning_content") or "").strip()
    return content
