"""
@RAG_CONTEXT
{
  "archivo": "src/rag_indexer/llm_client.py",
  "lineas": 119,
  "tokens_estimados": 1091,
  "hash_contenido": "539780638a7ab6aa6ce416fc0127120fccbdda08cb1d8c8b2f6e8d59501c8404",
  "creacion": "2026-03-04",
  "modificacion": "2026-03-09",
  "tags": [
    "json",
    "re",
    "os",
    "ACTIVE_LLM",
    "get_logger",
    ".logger",
    ".config",
    "requests",
    "time"
  ],
  "descripcion_corta": "Cliente REST multi-LLM con routing explícito por proveedor",
  "descripcion_larga": "Cliente HTTP puro (solo requests). get_llm_descriptions() resuelve API Key con precedencia RAG_API_KEY > clave específica. Routing if/elif documentado: openai/deepseek → Bearer token, anthropic → x-api-key, gemini → query param. Logger en DEBUG muestra proveedor+modelo por llamada. Stack trace completo en logger.exception() ante fallos de API."
}
"""
import os
import re
import json
import time
import requests
from dotenv import load_dotenv
from .config import ACTIVE_LLM, USE_CODE_MODEL, MODELS
from .logger import get_logger

logger = get_logger("rag_indexer_llm_client")


def clean_json_response(text):
    match = re.search(r'\{.*\}', text, re.DOTALL)
    return match.group(0) if match else text


def get_llm_descriptions(code, filename):
    """
    Genera descripciones técnicas para un archivo de código vía API del LLM activo.

    Enrutamiento por proveedor (RAG_AI_PROVIDER > ACTIVE_LLM > 'openai'):
      - "openai"    → api.openai.com/v1/chat/completions         [implementado]
      - "deepseek"  → api.deepseek.com/chat/completions          [implementado]
      - "anthropic" → api.anthropic.com/v1/messages              [implementado]
      - "gemini"    → generativelanguage.googleapis.com/v1beta/  [implementado]

    La API Key se resuelve en orden de precedencia:
      1. RAG_API_KEY (seteada por `rag-indexer api`)
      2. Clave específica del proveedor (OPENAI_API_KEY, ANTHROPIC_API_KEY, etc.)
    """
    # Garantiza que .env esté cargado en tiempo de ejecución (no solo al importar el módulo).
    # Necesario si config.py fue importado con un cwd distinto al directorio del proyecto.
    load_dotenv(os.path.join(os.getcwd(), '.env'))

    # Re-leer provider y api_key desde os.environ: config.py se evaluó en importación
    # y puede haber quedado con valores vacíos si el .env no estaba disponible en ese momento.
    # Precedencia de provider: RAG_AI_PROVIDER > ACTIVE_LLM > 'openai'
    _provider = os.environ.get("RAG_AI_PROVIDER", "").strip().lower() or ACTIVE_LLM

    _KEY_ENV = {
        "openai":    "OPENAI_API_KEY",
        "anthropic": "ANTHROPIC_API_KEY",
        "gemini":    "GEMINI_API_KEY",
        "deepseek":  "DEEPSEEK_API_KEY",
    }
    _rag_key      = os.environ.get("RAG_API_KEY", "").strip()
    _specific_key = os.environ.get(_KEY_ENV.get(_provider, ""), "").strip()
    api_key       = _rag_key or _specific_key

    tier = "code" if USE_CODE_MODEL else "default"
    model = MODELS.get(_provider, {}).get(tier)

    if not api_key or not model:
        return "", ""

    logger.debug(
        "Solicitando descripciones | archivo=%s, proveedor=%s, modelo=%s",
        filename, _provider, model,
    )

    prompt = (
        f"Eres un Analista de Arquitectura. Analiza el código de '{filename}'. "
        f"Devuelve ÚNICAMENTE un JSON válido sin markdown: "
        f'{{ "descripcion_corta": "Máx 50 chars.", "descripcion_larga": "100-500 chars, técnico." }}'
        f"\nCódigo:\n{code[:4000]}"
    )

    logger.debug(
        "Enviando petición a %s con llave: %s***",
        _provider, api_key[:5] if api_key else "(vacía)"
    )

    try:
        if _provider in ["openai", "deepseek"]:
            url = (
                "https://api.openai.com/v1/chat/completions"
                if _provider == "openai"
                else "https://api.deepseek.com/chat/completions"
            )
            headers = {
                "Authorization": f"Bearer {api_key}",
                "Content-Type": "application/json"
            }
            payload = {
                "model": model,
                "messages": [{"role": "user", "content": prompt}],
                "response_format": {"type": "json_object"}
            }
            res = requests.post(url, json=payload, headers=headers, timeout=30)
            res.raise_for_status()
            response_text = res.json()['choices'][0]['message']['content']

        elif _provider == "anthropic":
            headers = {
                "x-api-key": api_key,
                "anthropic-version": "2023-06-01",
                "content-type": "application/json"
            }
            payload = {
                "model": model,
                "max_tokens": 500,
                "messages": [{"role": "user", "content": prompt}]
            }
            res = requests.post(
                "https://api.anthropic.com/v1/messages",
                json=payload,
                headers=headers,
                timeout=30
            )
            res.raise_for_status()
            response_text = res.json()['content'][0]['text']

        elif _provider == "gemini":
            url = (
                f"https://generativelanguage.googleapis.com/v1beta/models"
                f"/{model}:generateContent?key={api_key}"
            )
            payload = {
                "contents": [{"parts": [{"text": prompt}]}],
                "generationConfig": {"responseMimeType": "application/json"}
            }
            res = requests.post(
                url,
                json=payload,
                headers={"Content-Type": "application/json"},
                timeout=30
            )
            res.raise_for_status()
            response_text = res.json()['candidates'][0]['content']['parts'][0]['text']

        else:
            logger.error("LLM no soportado: %s", _provider)
            return "", ""

        time.sleep(1.5)
        result = json.loads(clean_json_response(response_text))
        return result.get("descripcion_corta", ""), result.get("descripcion_larga", "")

    except Exception:
        logger.exception("Error LLM | proveedor=%s", _provider)
        return "", ""
