"""Ollama LLM provider — async HTTP streaming to localhost."""

from __future__ import annotations

import json
import logging
from collections.abc import AsyncIterator

import httpx

from redhat_oracle.core.llm.base import LLMProvider

logger = logging.getLogger(__name__)


class OllamaUnavailableError(Exception):
    """Raised when Ollama server is unreachable."""


class OllamaLLM(LLMProvider):
    """Ollama LLM provider via HTTP API."""

    def __init__(
        self,
        base_url: str = "http://localhost:11434",
        model: str = "llama3.2",
    ) -> None:
        self._base_url = base_url.rstrip("/")
        self._model = model

    async def _check_available(self) -> None:
        """Check if Ollama is reachable."""
        try:
            async with httpx.AsyncClient(timeout=5.0) as client:
                resp = await client.get(f"{self._base_url}/api/tags")
                resp.raise_for_status()
        except (httpx.ConnectError, httpx.TimeoutException, httpx.HTTPStatusError) as e:
            raise OllamaUnavailableError(
                f"Ollama is not reachable at {self._base_url}: {e}"
            ) from e

    async def generate(self, prompt: str, system: str | None = None) -> str:
        """Generate a complete response from Ollama."""
        chunks: list[str] = []
        async for chunk in self.stream(prompt, system):
            chunks.append(chunk)
        return "".join(chunks)

    async def stream(self, prompt: str, system: str | None = None) -> AsyncIterator[str]:
        """Stream response tokens from Ollama."""
        payload: dict[str, object] = {
            "model": self._model,
            "prompt": prompt,
            "stream": True,
        }
        if system:
            payload["system"] = system

        try:
            async with httpx.AsyncClient(  # noqa: SIM117
                timeout=httpx.Timeout(30.0, read=300.0),
            ) as client:
                async with client.stream(
                    "POST",
                    f"{self._base_url}/api/generate",
                    json=payload,
                ) as response:
                    response.raise_for_status()
                    async for line in response.aiter_lines():
                        if not line.strip():
                            continue
                        try:
                            data = json.loads(line)
                        except json.JSONDecodeError:
                            continue

                        if "response" in data:
                            yield data["response"]

                        if data.get("done", False):
                            return
        except (httpx.ConnectError, httpx.TimeoutException) as e:
            raise OllamaUnavailableError(
                f"Ollama is not reachable at {self._base_url}: {e}"
            ) from e
