"""Ejemplo educativo: inventario ficticio, sin escrituras ni memoria durable.
Python 3.10+. Para API real: pip install openai; OPENAI_API_KEY y OPENAI_MODEL.
Las pruebas locales usan un cliente simulado, no consumen tokens.
"""
import json
import os

STOCK = {"A-100": 23, "B-200": 4, "C-300": 0}
TOOLS = [{
    "type": "function",
    "name": "consultar_stock",
    "description": "Consulta inventario de ejemplo por SKU; no reserva unidades.",
    "strict": True,
    "parameters": {
        "type": "object",
        "properties": {"sku": {"type": "string"}},
        "required": ["sku"],
        "additionalProperties": False,
    },
}]


def ejecutar(nombre, argumentos):
    if nombre != "consultar_stock":
        return {"error": "herramienta_no_permitida"}
    try:
        datos = json.loads(argumentos)
    except (ValueError, TypeError):
        return {"error": "json_invalido"}
    if not isinstance(datos, dict) or set(datos) != {"sku"}:
        return {"error": "argumentos_invalidos"}
    sku = datos["sku"]
    if not isinstance(sku, str) or len(sku) > 32:
        return {"error": "sku_invalido"}
    if sku not in STOCK:
        return {"error": "sku_desconocido", "sku": sku}
    return {"sku": sku, "stock": STOCK[sku]}


def responder(cliente, pregunta, modelo, max_pasos=4):
    historial = [{"role": "user", "content": pregunta}]
    for _ in range(max_pasos):
        respuesta = cliente.responses.create(
            model=modelo,
            instructions="Consulta stock con la herramienta. No inventes existencias ni reserves productos.",
            input=historial,
            tools=TOOLS,
            parallel_tool_calls=False,
            max_output_tokens=500,
        )
        # Preserva también los items de razonamiento que algunos modelos necesitan.
        historial.extend(respuesta.output)
        llamadas = [item for item in respuesta.output if item.type == "function_call"]
        if not llamadas:
            if not respuesta.output_text:
                raise RuntimeError("Respuesta vacía o incompleta: revisar estado del proveedor")
            return respuesta.output_text
        # Tope defensivo adicional, aunque se pidió desactivar llamadas paralelas.
        if len(llamadas) > 4:
            raise RuntimeError("Demasiadas herramientas en un paso")
        for llamada in llamadas:
            resultado = ejecutar(llamada.name, llamada.arguments)
            historial.append({
                "type": "function_call_output",
                "call_id": llamada.call_id,
                "output": json.dumps(resultado, ensure_ascii=False),
            })
    raise RuntimeError("Límite de pasos alcanzado; no seguir reintentando")


if __name__ == "__main__":
    from openai import OpenAI
    modelo = os.environ.get("OPENAI_MODEL")
    if not modelo:
        raise SystemExit("Configura OPENAI_MODEL con un modelo habilitado para function calling")
    cliente = OpenAI(timeout=20.0, max_retries=0)
    print(responder(cliente, "¿Cuántas unidades hay del A-100?", modelo))
