"""LLM provider abstraction (OpenAI-compatible API) with tool calling."""

from __future__ import annotations

import json

from openai import OpenAI

from clawsy.config import LLMConfig
from clawsy.tools import TOOL_DEFINITIONS, TOOL_HANDLERS


def create_client(cfg: LLMConfig) -> OpenAI:
    """Create an OpenAI-compatible client from config."""
    return OpenAI(
        api_key=cfg.api_key,
        base_url=cfg.base_url,
    )


def generate_patch(
    client: OpenAI,
    model: str,
    program_md: str,
    enriched_prompt: str = "",
    use_tools: bool = False,
) -> str:
    """Ask LLM to generate an improvement patch, optionally with tools.

    If enriched_prompt is provided (from platform), use it as system prompt.
    If use_tools is True, enable web_search and fetch_url via function calling.
    """
    system = enriched_prompt if enriched_prompt else (
        "You are an optimization agent. Given a program description, "
        "generate a concrete improvement. Output your result as JSON with fields: "
        "improved_content, changes (list of {what, why}), and metrics (before/after numbers)."
    )

    messages = [
        {"role": "system", "content": system},
        {"role": "user", "content": program_md},
    ]

    kwargs: dict = {
        "model": model,
        "messages": messages,
        "temperature": 0.7,
    }

    if use_tools and TOOL_DEFINITIONS:
        kwargs["tools"] = TOOL_DEFINITIONS

    # Tool calling loop (max 5 rounds to prevent infinite loops)
    for _ in range(5):
        resp = client.chat.completions.create(**kwargs)
        msg = resp.choices[0].message

        # If no tool calls, we're done
        if not msg.tool_calls:
            return msg.content or ""

        # Process tool calls
        messages.append(msg)  # type: ignore[arg-type]
        for tool_call in msg.tool_calls:
            fn_name = tool_call.function.name
            try:
                fn_args = json.loads(tool_call.function.arguments)
            except json.JSONDecodeError:
                fn_args = {}

            handler = TOOL_HANDLERS.get(fn_name)
            if handler:
                result = handler(fn_args)
            else:
                result = f"Unknown tool: {fn_name}"

            messages.append({
                "role": "tool",
                "tool_call_id": tool_call.id,
                "content": str(result),
            })

        kwargs["messages"] = messages

    # If we hit max rounds, return whatever we have
    resp = client.chat.completions.create(**kwargs)
    return resp.choices[0].message.content or ""
