"""
06 - EXPERIMENTO: descripciones solapadas. ¿Se confunde el modelo?

A propósito se meten TRES herramientas de búsqueda casi sinónimas, con
descripciones que se pisan. Las tres hacen lo mismo (consultan SearXNG), pero
se quiere ver QUÉ elige el modelo y si es CONSISTENTE entre preguntas parecidas.

Hipótesis: con descripciones solapadas el modelo elige de forma arbitraria o
inconsistente, porque no hay un disparador claro que las distinga.

Cada herramienta marca su resultado con [origen: NOMBRE] para ver cuál usó.

Requisito: SearXNG levantado ->  cd searxng && docker compose up -d
"""

import json
import re

import requests
import ollama

MODELO = "qwen2.5:14b-instruct-q4_K_M"
SEARXNG_URL = "http://localhost:8888/search"


def _buscar(nombre_origen: str, query: str) -> str:
    resp = requests.get(SEARXNG_URL, params={"q": query, "format": "json"}, timeout=20)
    resp.raise_for_status()
    resultados = resp.json().get("results", [])[:3]
    cuerpo = " | ".join(r.get("title", "")[:50] for r in resultados) or "sin resultados"
    return f"[origen: {nombre_origen}] {cuerpo}"


# Tres funciones distintas... que hacen EXACTAMENTE lo mismo.
def buscar_web(query: str) -> str:      return _buscar("buscar_web", query)
def buscar_datos(query: str) -> str:    return _buscar("buscar_datos", query)
def buscar_general(query: str) -> str:  return _buscar("buscar_general", query)


FUNCIONES = {
    "buscar_web": buscar_web,
    "buscar_datos": buscar_datos,
    "buscar_general": buscar_general,
}

# Descripciones SOLAPADAS a propósito: las tres sirven para casi todo.
TOOLS = [
    {"type": "function", "function": {
        "name": "buscar_web",
        "description": "Busca información actualizada en internet.",
        "parameters": {"type": "object",
                       "properties": {"query": {"type": "string", "description": "Términos de búsqueda."}},
                       "required": ["query"]}}},
    {"type": "function", "function": {
        "name": "buscar_datos",
        "description": "Busca datos y hechos en internet. Úsala para versiones, fechas y cifras.",
        "parameters": {"type": "object",
                       "properties": {"query": {"type": "string", "description": "Términos de búsqueda."}},
                       "required": ["query"]}}},
    {"type": "function", "function": {
        "name": "buscar_general",
        "description": "Búsqueda general en internet para responder cualquier pregunta.",
        "parameters": {"type": "object",
                       "properties": {"query": {"type": "string", "description": "Términos de búsqueda."}},
                       "required": ["query"]}}},
]

SYSTEM = "Eres un asistente con acceso a internet. Usa una herramienta de búsqueda para responder."


def llamadas_fugadas_en_texto(contenido: str):
    if not contenido:
        return []
    out = []
    for b in re.findall(r'\{[^{}]*"name"[^{}]*\{[^{}]*\}[^{}]*\}', contenido):
        try:
            o = json.loads(b)
            if "name" in o and "arguments" in o:
                out.append((o["name"], o["arguments"]))
        except json.JSONDecodeError:
            continue
    return out


def responder(pregunta: str, max_turnos: int = 5):
    print(f"\n{'='*70}\nPREGUNTA: {pregunta}\n{'='*70}")
    messages = [
        {"role": "system", "content": SYSTEM},
        {"role": "user", "content": pregunta},
    ]
    elegidas = []
    for _ in range(max_turnos):
        r = ollama.chat(model=MODELO, messages=messages, tools=TOOLS, options={"temperature": 0})
        messages.append(r.message)
        pend = [(tc.function.name, tc.function.arguments) for tc in (r.message.tool_calls or [])]
        if not pend:
            pend = llamadas_fugadas_en_texto(r.message.content)
        if not pend:
            print(f"   herramientas elegidas: {elegidas}")
            return
        for nombre, args in pend:
            elegidas.append(nombre)
            print(f"   --> elige: {nombre}")
            f = FUNCIONES.get(nombre)
            res = f(**args) if f else f"Error: '{nombre}' desconocida"
            messages.append({"role": "tool", "name": nombre, "content": str(res)})


if __name__ == "__main__":
    # Preguntas parecidas: ¿elegirá siempre la misma herramienta? ¿la coherente?
    responder("¿Cuál es la última versión estable de Python?")     # 'versiones' apunta a buscar_datos
    responder("¿Cuál es la última versión estable de Node.js?")    # idéntica naturaleza
    responder("¿Quién ganó el último Mundial de fútbol?")          # no es 'versión/fecha/cifra'
    responder("¿Cuántos habitantes tiene Madrid?")                 # 'cifra' -> ¿buscar_datos?
