"""
LLM backend implementations.

Provides pluggable LLM backends: NoOp (echo), OpenAI, Anthropic, Gemini.
"""

from __future__ import annotations


class NoOpLLM:
    """
    No-op LLM backend that echoes prompts.
    
    Useful for testing and local dev without API keys.
    """

    async def generate(self, prompt: str, system: str | None = None, **kwargs) -> str:
        """Echo the prompt as response."""
        return f"[NoOpLLM Echo] Prompt: {prompt[:100]}..."


class OpenAILLM:
    """OpenAI LLM backend."""

    def __init__(self, api_key: str | None, model: str | None = None, base_url: str | None = None):
        self.api_key = api_key
        self.default_model = model or "gpt-4"
        self.base_url = base_url
        self._client = None

    async def generate(self, prompt: str, system: str | None = None, **kwargs) -> str:
        """Generate response using OpenAI."""
        if not self.api_key:
            raise ValueError("OpenAI API key not configured")

        if self._client is None:
            try:
                import openai
                kwargs = {"api_key": self.api_key}
                if self.base_url:
                    kwargs["base_url"] = self.base_url
                self._client = openai.AsyncOpenAI(**kwargs)
            except ImportError:
                raise RuntimeError("openai package not installed. Install with: pip install openai")

        messages = []
        if system:
            messages.append({"role": "system", "content": system})
        messages.append({"role": "user", "content": prompt})

        response = await self._client.chat.completions.create(
            model=kwargs.get("model", self.default_model),
            messages=messages,
            temperature=kwargs.get("temperature", 0.7),
            max_tokens=kwargs.get("max_tokens", 1000)
        )

        return response.choices[0].message.content

    async def generate_batch(self, prompts: list[str], system: str | None = None, **kwargs) -> list[str]:
        # Fallback: run sequentially
        results: list[str] = []
        for p in prompts:
            results.append(await self.generate(p, system=system, **kwargs))
        return results


class AnthropicLLM:
    """Anthropic LLM backend."""

    def __init__(self, api_key: str | None, model: str | None = None):
        self.api_key = api_key
        self.default_model = model or "claude-3-sonnet-20240229"
        self._client = None

    async def generate(self, prompt: str, system: str | None = None, **kwargs) -> str:
        """Generate response using Anthropic."""
        if not self.api_key:
            raise ValueError("Anthropic API key not configured")

        if self._client is None:
            try:
                import anthropic
                self._client = anthropic.AsyncAnthropic(api_key=self.api_key)
            except ImportError:
                raise RuntimeError("anthropic package not installed. Install with: pip install anthropic")

        response = await self._client.messages.create(
            model=kwargs.get("model", self.default_model),
            max_tokens=kwargs.get("max_tokens", 1000),
            system=system or "",
            messages=[{"role": "user", "content": prompt}]
        )

        return response.content[0].text


class GeminiLLM:
    """Google Gemini LLM backend."""

    def __init__(self, api_key: str | None, model: str | None = None):
        self.api_key = api_key
        self.default_model = model or "gemini-2.5-flash"
        self._model = None

    async def generate(self, prompt: str, system: str | None = None, **kwargs) -> str:
        """Generate response using Gemini."""
        if not self.api_key:
            raise ValueError("Gemini API key not configured")

        if self._model is None:
            try:
                import google.generativeai as genai
                genai.configure(api_key=self.api_key)
                self._model = genai.GenerativeModel(kwargs.get("model", self.default_model))
            except ImportError:
                raise RuntimeError("google-generativeai package not installed. Install with: pip install google-generativeai")

        full_prompt = prompt
        if system:
            full_prompt = f"{system}\n\n{prompt}"

        response = await self._model.generate_content_async(full_prompt)
        return response.text

    async def generate_batch(self, prompts: list[str], system: str | None = None, **kwargs) -> list[str]:
        # Fallback sequentially
        results: list[str] = []
        for p in prompts:
            results.append(await self.generate(p, system=system, **kwargs))
        return results


class HTTPLLM:
    """Generic HTTP LLM adapter expecting a simple JSON API.

    It POSTs to endpoint with body: {"prompt": str, "system": str | null, "params": {...}}
    and expects a JSON response with {"text": str}.
    """

    def __init__(self, endpoint: str):
        self.endpoint = endpoint

    async def generate(self, prompt: str, system: str | None = None, **kwargs) -> str:
        import json
        import urllib.error
        import urllib.request
        body = json.dumps({"prompt": prompt, "system": system, "params": kwargs}).encode("utf-8")

        def _do_request():
            req = urllib.request.Request(self.endpoint, data=body, headers={"Content-Type": "application/json"})
            with urllib.request.urlopen(req, timeout=30) as resp:
                return resp.read().decode("utf-8")

        try:
            raw = await asyncio.to_thread(_do_request)  # type: ignore[name-defined]
            data = json.loads(raw)
            return data.get("text", "")
        except Exception as e:  # pragma: no cover - network dependent
            raise RuntimeError(f"HTTP LLM request failed: {e}")

    async def generate_batch(self, prompts: list[str], system: str | None = None, **kwargs) -> list[str]:
        # Fallback: sequential calls
        results: list[str] = []
        for p in prompts:
            results.append(await self.generate(p, system=system, **kwargs))
        return results
