"""Ollama LLM client with retry and response cleaning."""

import json
import logging
import re
import time
import urllib.request
import urllib.error

log = logging.getLogger(__name__)

DEFAULT_URL = "http://192.168.0.145:31434"
DEFAULT_MODEL = "qwen3-coder:latest"


class OllamaUnavailableError(Exception):
    pass


def ollama_available(url: str = DEFAULT_URL) -> bool:
    """Quick health check — GET /api/tags."""
    try:
        req = urllib.request.Request(f"{url.rstrip('/')}/api/tags")
        with urllib.request.urlopen(req, timeout=5):
            return True
    except Exception:
        return False


_last_call_time = 0.0
MIN_CALL_INTERVAL = 2.0  # seconds between calls to avoid overwhelming Ollama


def ollama_generate(
    prompt: str,
    model: str = DEFAULT_MODEL,
    url: str = DEFAULT_URL,
    max_retries: int = 3,
    timeout: int = 120,
    temperature: float = 0.3,
    num_predict: int = 2000,
) -> str:
    """Generate text from Ollama with retry + backoff. Returns cleaned response."""
    global _last_call_time
    elapsed = time.time() - _last_call_time
    if elapsed < MIN_CALL_INTERVAL:
        time.sleep(MIN_CALL_INTERVAL - elapsed)
    _last_call_time = time.time()

    data = json.dumps({
        "model": model,
        "prompt": prompt,
        "stream": False,
        "options": {"temperature": temperature, "num_predict": num_predict},
    }).encode()
    req = urllib.request.Request(
        f"{url.rstrip('/')}/api/generate",
        data=data,
        headers={"Content-Type": "application/json"},
    )

    last_error = None
    for attempt in range(max_retries):
        try:
            with urllib.request.urlopen(req, timeout=timeout) as resp:
                result = json.loads(resp.read())
                raw = result.get("response", "").strip()
                return re.sub(r"<think>.*?</think>", "", raw, flags=re.DOTALL).strip()
        except (urllib.error.URLError, TimeoutError, OSError) as e:
            last_error = e
            if attempt < max_retries - 1:
                wait = 2 ** attempt
                log.warning("Ollama attempt %d/%d failed: %s — retrying in %ds",
                            attempt + 1, max_retries, e, wait)
                time.sleep(wait)

    raise OllamaUnavailableError(f"Ollama unavailable after {max_retries} attempts: {last_error}")
