"""
GPT4Free (g4f) Provider — Unofficial web account wrapper.
"""

from __future__ import annotations

import json
from typing import Any

from neuralclaw.providers.router import LLMProvider, LLMResponse, ToolCall

try:
    import g4f
    from g4f.client import AsyncClient
except ImportError:
    g4f = None
    AsyncClient = None


class G4FProvider(LLMProvider):
    """Free web account wrapper provider using g4f."""

    name = "g4f"

    def __init__(
        self,
        api_key: str | None = None,
        model: str = "gpt-4",
        base_url: str = "",
    ) -> None:
        # We don't use the API key or base_url for G4F, it scrapes the web natively
        self._model = model
        self._client = AsyncClient() if AsyncClient else None

    async def complete(
        self,
        messages: list[dict[str, Any]],
        tools: list[dict[str, Any]] | None = None,
        temperature: float = 0.7,
        max_tokens: int = 4096,
    ) -> LLMResponse:
        """Route completion to g4f AsyncClient."""
        if not self._client:
            raise RuntimeError("g4f library is not installed. Run: pip install -U g4f curl_cffi")

        # G4F attempts to route to various free providers based on the model.
        # tools/function calls support varies heavily among free web providers, 
        # so we will strip them to prevent crash loops on unsupported endpoints.
        
        # We also might run into rate limits or captchas depending on the G4F backend's health.
        try:
            response = await self._client.chat.completions.create(
                model=self._model,
                messages=messages,
                # Temperature and max_tokens are passed but often ignored by free providers
            )
        except Exception as e:
            raise RuntimeError(f"G4F Web Scrape Error: {e}")

        # Map to LLMResponse
        if not response.choices:
            raise RuntimeError("G4F returned an empty or invalid response shape.")

        message = response.choices[0].message

        return LLMResponse(
            content=message.content,
            tool_calls=None, # Function calling usually not reliably supported on free web tiers
            model=self._model,
            usage={}, # Free tiers don't typically return token math
            raw={"source": "g4f", "raw": response.to_dict() if hasattr(response, 'to_dict') else {}}
        )

    async def is_available(self) -> bool:
        """Check if the internal library is loaded."""
        return self._client is not None
