"""llama.cpp LLM provider for Local Deep Research.

Talks to llama.cpp's OpenAI-compatible HTTP server (`llama-server`) instead
of loading models in-process via `llama-cpp-python`. Modeled after
`LMStudioProvider`. For setups that need API key auth or non-default URLs
beyond a single endpoint, use the `openai_endpoint` provider directly.
"""

from ....config.constants import DEFAULT_LLAMACPP_URL
from ....utilities.url_utils import normalize_url
from ..openai_base import OpenAICompatibleProvider


class LlamaCppProvider(OpenAICompatibleProvider):
    """llama.cpp provider using its OpenAI-compatible HTTP endpoint.

    Run `llama-server -m <model.gguf>` (port 8080 by default) and point
    `llm.llamacpp.url` at the server's `/v1` endpoint.
    """

    provider_name = "llama.cpp"
    # llama-server HAS an API key concept (for setups behind an auth proxy);
    # api_key_optional makes the base resolver fall back to a placeholder
    # when no key is set, instead of raising.
    api_key_setting = "llm.llamacpp.api_key"
    api_key_optional = True
    url_setting = "llm.llamacpp.url"  # type: ignore[assignment]
    default_base_url = DEFAULT_LLAMACPP_URL
    default_model = ""  # User must specify the model loaded by llama-server

    # Metadata for auto-discovery
    provider_key = "LLAMACPP"
    company_name = "llama.cpp"
    is_cloud = False  # Local provider

    @classmethod
    def create_llm(cls, model_name=None, temperature=0.7, **kwargs):
        """Create a ChatOpenAI client pointed at llama-server."""
        from ....config.thread_settings import get_setting_from_snapshot

        settings_snapshot = kwargs.get("settings_snapshot")

        url = get_setting_from_snapshot(
            "llm.llamacpp.url",
            cls.default_base_url,
            settings_snapshot=settings_snapshot,
        )

        kwargs["base_url"] = normalize_url(url)
        # Real key when configured (llama-server behind an auth proxy),
        # otherwise the unified placeholder; a no-auth llama-server
        # ignores it.
        kwargs["api_key"] = cls.resolve_api_key_or_placeholder(
            settings_snapshot
        )  # gitleaks:allow

        return super()._create_llm_instance(model_name, temperature, **kwargs)

    @classmethod
    def is_available(cls, settings_snapshot=None):
        """Check whether llama-server is reachable.

        Sends ``Authorization: Bearer`` when an API key is configured so
        llama-server instances behind an auth proxy are correctly detected
        as available. Empty key → no auth header → unauthenticated installs
        still work. Mirrors the LMStudio pattern at lmstudio.py:_get_auth_headers.
        """
        try:
            from ....config.thread_settings import get_setting_from_snapshot
            from ....security import safe_get

            url = get_setting_from_snapshot(
                "llm.llamacpp.url",
                cls.default_base_url,
                settings_snapshot=settings_snapshot,
            )
            base_url = normalize_url(url)
            response = safe_get(
                f"{base_url}/models",
                timeout=1,
                headers=cls.build_bearer_header(
                    settings_snapshot=settings_snapshot
                ),
                allow_localhost=True,
                allow_private_ips=True,
            )
            return response.status_code == 200
        except Exception:
            return False

    @classmethod
    def requires_auth_for_models(cls):
        """llama-server doesn't require authentication for listing models."""
        return False
