"""
02 - Tool calling REAL: el modelo busca en internet lo que no sabe.

Mismo mecanismo que 01, pero:
  - La herramienta ahora es 'buscar_web' (DuckDuckGo, sin API key).
  - Se usa un BUCLE: el modelo puede pedir varias búsquedas seguidas antes
    de responder. Un agente de verdad casi nunca resuelve en un solo turno.

Cómo se "entera de lo que no sabe":
  El modelo fue entrenado con fecha de corte. Si le preguntas algo posterior
  o muy específico, no lo sabe -> se describe una herramienta de búsqueda y él
  decide llamarla. Se le devuelven los resultados y responde basándose en ellos.
"""

import ollama
from ddgs import DDGS

# Generalista razona mejor CUÁNDO buscar. Puede cambiarse a "qwen2.5-coder:14b-instruct-q4_K_M"
# si las preguntas son técnicas/de código.
MODELO = "qwen2.5:14b-instruct-q4_K_M"


# --- La función real de búsqueda ------------------------------------------------

def buscar_web(query: str) -> str:
    print(f"   [Python busca en DuckDuckGo] '{query}'")
    with DDGS() as ddgs:
        resultados = list(ddgs.text(query, max_results=5))
    if not resultados:
        return "No se encontraron resultados."
    # Se devuelve texto compacto: título + resumen + url. Esto es lo que "lee" el modelo.
    return "\n\n".join(
        f"[{i+1}] {r['title']}\n{r['body']}\nFuente: {r['href']}"
        for i, r in enumerate(resultados)
    )


FUNCIONES = {"buscar_web": buscar_web}

TOOLS = [
    {
        "type": "function",
        "function": {
            "name": "buscar_web",
            "description": (
                "Busca información actualizada en internet. Úsala SIEMPRE que la "
                "pregunta trate sobre hechos recientes, fechas, versiones, precios, "
                "noticias o cualquier cosa que no sepas con certeza."
            ),
            "parameters": {
                "type": "object",
                "properties": {
                    "query": {
                        "type": "string",
                        "description": "Términos de búsqueda, concisos y en el idioma adecuado.",
                    }
                },
                "required": ["query"],
            },
        },
    }
]

SYSTEM = (
    "Eres un asistente útil. Si no estás seguro de un dato o puede haber cambiado "
    "tras tu entrenamiento, USA la herramienta buscar_web antes de responder. "
    "Cita las fuentes (URLs) que uses en tu respuesta final."
)


def responder(pregunta: str, max_turnos: int = 5):
    messages = [
        {"role": "system", "content": SYSTEM},
        {"role": "user", "content": pregunta},
    ]

    for turno in range(max_turnos):
        respuesta = ollama.chat(model=MODELO, messages=messages, tools=TOOLS)
        messages.append(respuesta.message)

        # Si no pide herramientas, ya tiene la respuesta final.
        if not respuesta.message.tool_calls:
            print("\nRESPUESTA FINAL:\n")
            print(respuesta.message.content)
            return

        # Se ejecuta cada herramienta pedida y se le devuelve el resultado.
        for tc in respuesta.message.tool_calls:
            funcion = FUNCIONES.get(tc.function.name)
            if funcion is None:
                resultado = f"Error: herramienta desconocida '{tc.function.name}'"
            else:
                resultado = funcion(**tc.function.arguments)
            messages.append({
                "role": "tool",
                "name": tc.function.name,
                "content": str(resultado),
            })

    print("Se alcanzó el máximo de turnos sin respuesta final.")


if __name__ == "__main__":
    # Una pregunta que el modelo NO puede saber de memoria:
    responder("¿Qué versión estable de Python es la más reciente y cuándo salió?")
