"""Construct providers + clients from model ids, and enumerate the registry."""

from __future__ import annotations

from pentestgpt_legacy.llm.base import BaseProvider
from pentestgpt_legacy.llm.client import LLMClient
from pentestgpt_legacy.llm.config import get_settings
from pentestgpt_legacy.llm.providers import (
    AnthropicProvider,
    GeminiProvider,
    OpenAICompatibleProvider,
)
from pentestgpt_legacy.llm.registry import (
    MODELS,
    PROVIDERS,
    ModelSpec,
    ProviderInfo,
    all_model_ids,
    resolve,
)

_PROVIDER_CLASSES: dict[str, type[BaseProvider]] = {
    "openai": OpenAICompatibleProvider,
    "anthropic": AnthropicProvider,
    "gemini": GeminiProvider,
}


class UnknownModelError(ValueError):
    """Raised when a model id is not in the registry."""


class MissingCredentialsError(RuntimeError):
    """Raised when a provider's API key is not configured."""


def _build_provider(info: ProviderInfo) -> BaseProvider:
    settings = get_settings()
    api_key = settings.api_key_for(info)
    if info.requires_key and not api_key:
        env_names = ", ".join([info.env, *info.env_alt]) if info.env else "(none)"
        raise MissingCredentialsError(
            f"No API key for provider '{info.key}' ({info.label}). "
            f"Set one of: {env_names} in your environment or .env file."
        )
    base_url = settings.base_url_for(info)
    provider_cls = _PROVIDER_CLASSES[info.kind]
    return provider_cls(info, api_key, base_url)


def get_client(
    model_name: str,
    system_prompt: str | None = None,
    history_length: int = 40,
) -> LLMClient:
    """Build an :class:`LLMClient` for ``model_name``.

    Accepts any registry id/alias, plus the dynamic ``ollama:<model>`` form.
    Raises :class:`UnknownModelError` for unknown ids and
    :class:`MissingCredentialsError` when the provider has no key configured.
    """
    spec = resolve(model_name)
    if spec is None:
        available = ", ".join(all_model_ids())
        raise UnknownModelError(
            f"Unknown model '{model_name}'. Available: {available} "
            f"(or 'ollama:<model>' for local models)."
        )
    info = PROVIDERS[spec.provider]
    provider = _build_provider(info)
    return LLMClient(provider, spec, system_prompt=system_prompt, history_length=history_length)


def list_models() -> list[ModelSpec]:
    """Return all registry models in display order."""
    return list(MODELS.values())
