"""Local Ollama client for mail analysis."""

from __future__ import annotations

import json
import logging
import re
from typing import Any

import httpx

from config import LLM_BASE_URL, LLM_MODEL, LLM_TIMEOUT_SECONDS

logger = logging.getLogger("mail_summary")

_JSON_BLOCK_RE = re.compile(r"\{.*\}", re.DOTALL)


class LlmError(RuntimeError):
    pass


def extract_json_object(text: str) -> dict[str, Any]:
    text = (text or "").strip()
    if not text:
        raise LlmError("empty_llm_response")
    try:
        data = json.loads(text)
        if isinstance(data, dict):
            return data
    except json.JSONDecodeError:
        pass
    match = _JSON_BLOCK_RE.search(text)
    if not match:
        raise LlmError("llm_response_not_json")
    data = json.loads(match.group(0))
    if not isinstance(data, dict):
        raise LlmError("llm_response_not_object")
    return data


async def chat_json(
    *,
    system: str,
    user: str,
    model: str | None = None,
    temperature: float = 0.2,
) -> dict[str, Any]:
    selected = (model or LLM_MODEL).strip() or LLM_MODEL
    payload = {
        "model": selected,
        "stream": False,
        "format": "json",
        "options": {"temperature": temperature},
        "messages": [
            {"role": "system", "content": system},
            {"role": "user", "content": user},
        ],
    }
    url = f"{LLM_BASE_URL}/api/chat"
    try:
        async with httpx.AsyncClient(timeout=LLM_TIMEOUT_SECONDS) as client:
            response = await client.post(url, json=payload)
            response.raise_for_status()
            body = response.json()
    except httpx.HTTPError as exc:
        logger.exception("Ollama request failed")
        raise LlmError(f"ollama_request_failed: {exc}") from exc

    content = (
        (body.get("message") or {}).get("content")
        or body.get("response")
        or ""
    )
    return extract_json_object(content)


async def ping_llm(model: str | None = None) -> dict[str, Any]:
    selected = (model or LLM_MODEL).strip() or LLM_MODEL
    try:
        async with httpx.AsyncClient(timeout=3.0) as client:
            tags = await client.get(f"{LLM_BASE_URL}/api/tags")
            tags.raise_for_status()
            data = tags.json()
    except Exception as exc:  # noqa: BLE001
        return {"ok": False, "error": str(exc), "model": selected}
    names = [m.get("name", "") for m in data.get("models", [])]
    return {
        "ok": True,
        "model": selected,
        "model_present": any(selected in name or name in selected for name in names),
        "models": names[:20],
    }
