"""LLM gateway for GreyMatter — Ollama integration with fallback chain.

Primary: local Ollama (localhost:11434, nemotron-3-nano)
Fallback: Mac Mini Ollama (100.81.247.14:11434, gpt-oss:20b)

Config via env vars:
    GREYMATTER_LLM_URL          — primary Ollama URL
    GREYMATTER_LLM_FALLBACK_URL — fallback Ollama URL
    GREYMATTER_LLM_MODEL        — model name for extraction
"""
import json
import os
from typing import Any

import httpx


DEFAULT_URL = "http://localhost:11434"
DEFAULT_FALLBACK_URL = "http://100.81.247.14:11434"
DEFAULT_MODEL = "nemotron-3-nano"


class OllamaLLM:
    """Local LLM via Ollama API with fallback chain."""

    def __init__(
        self,
        model: str | None = None,
        base_url: str | None = None,
        fallback_url: str | None = None,
        timeout: float = 120.0,
    ):
        self.model = model or os.environ.get("GREYMATTER_LLM_MODEL", DEFAULT_MODEL)
        self.base_url = base_url or os.environ.get("GREYMATTER_LLM_URL", DEFAULT_URL)
        self.fallback_url = fallback_url or os.environ.get("GREYMATTER_LLM_FALLBACK_URL", DEFAULT_FALLBACK_URL)
        self.timeout = timeout

    def generate(self, prompt: str, system: str = "", temperature: float = 0.3) -> str:
        """Generate a completion. Tries primary, falls back to fallback URL."""
        payload: dict[str, Any] = {
            "model": self.model,
            "prompt": prompt,
            "stream": False,
            "options": {"temperature": temperature},
        }
        if system:
            payload["system"] = system

        # Try primary
        try:
            resp = httpx.post(
                f"{self.base_url}/api/generate",
                json=payload,
                timeout=self.timeout,
            )
            resp.raise_for_status()
            return resp.json().get("response", "")
        except (httpx.ConnectError, httpx.TimeoutException, httpx.HTTPStatusError):
            pass

        # Try fallback
        if self.fallback_url and self.fallback_url != self.base_url:
            resp = httpx.post(
                f"{self.fallback_url}/api/generate",
                json=payload,
                timeout=self.timeout,
            )
            resp.raise_for_status()
            return resp.json().get("response", "")

        raise ConnectionError("Both primary and fallback Ollama unreachable")

    def generate_json(self, prompt: str, system: str = "", temperature: float = 0.1) -> Any:
        """Generate and parse JSON response."""
        raw = self.generate(prompt, system=system, temperature=temperature)
        raw = raw.strip()
        if raw.startswith("```"):
            lines = raw.split("\n")
            raw = "\n".join(lines[1:-1] if lines[-1].strip() == "```" else lines[1:])
        return json.loads(raw)


    def chat(
        self,
        prompt,
        system='',
        temperature=0.3,
        think=True,
    ):
        """Chat completion via /api/chat with thinking support.

        Uses Ollama chat API which separates thinking tokens from output.

        Args:
            prompt: User message.
            system: System prompt.
            temperature: Sampling temperature.
            think: Enable chain-of-thought reasoning (separated from output).

        Returns:
            dict with 'content' (clean output) and 'thinking' (reasoning or None).
        """
        messages = []
        if system:
            messages.append({'role': 'system', 'content': system})
        messages.append({'role': 'user', 'content': prompt})

        payload = {
            'model': self.model,
            'messages': messages,
            'stream': False,
            'options': {'temperature': temperature},
            'think': think,
        }

        # Try primary
        try:
            resp = httpx.post(
                f'{self.base_url}/api/chat',
                json=payload,
                timeout=self.timeout,
            )
            resp.raise_for_status()
            msg = resp.json().get('message', {})
            return {'content': msg.get('content', ''), 'thinking': msg.get('thinking')}
        except (httpx.ConnectError, httpx.TimeoutException, httpx.HTTPStatusError):
            pass

        # Try fallback
        if self.fallback_url and self.fallback_url != self.base_url:
            resp = httpx.post(
                f'{self.fallback_url}/api/chat',
                json=payload,
                timeout=self.timeout,
            )
            resp.raise_for_status()
            msg = resp.json().get('message', {})
            return {'content': msg.get('content', ''), 'thinking': msg.get('thinking')}

        raise ConnectionError('Both primary and fallback Ollama unreachable')

    def chat_json(self, prompt, system="", temperature=0.1, think=True):
        """Chat completion returning parsed JSON with optional reasoning.

        Returns dict with result (parsed JSON) and thinking (reasoning).
        """
        result = self.chat(prompt, system=system, temperature=temperature, think=think)
        raw = result["content"].strip()
        if raw.startswith("\x60\x60\x60"):
            rawlines = raw.split("\n")
            raw = "\n".join(rawlines[1:-1] if rawlines[-1].strip() == "\x60\x60\x60" else rawlines[1:])
        return {"result": __import__("json").loads(raw), "thinking": result.get("thinking")}

    def is_available(self) -> bool:
        """Check if any Ollama instance is reachable."""
        for url in [self.base_url, self.fallback_url]:
            if not url:
                continue
            try:
                resp = httpx.get(f"{url}/api/tags", timeout=5.0)
                if resp.status_code == 200:
                    return True
            except Exception:
                continue
        return False
