"""Tests for ``list_picker_providers`` — the /model picker filter.

``list_picker_providers`` wraps ``list_authenticated_providers`` and
post-processes the result for interactive pickers (Telegram, Discord):

- OpenRouter's ``models`` are replaced with the live-filtered output of
  ``fetch_openrouter_models``, so IDs the live catalog no longer carries
  drop out.
- Provider rows with an empty ``models`` list are dropped, except custom
  endpoints (``is_user_defined=True`` with an ``api_url``) where the user
  may supply their own model set through config.

These tests exercise the filter in isolation by mocking
``list_authenticated_providers`` and ``fetch_openrouter_models`` so no
network or auth state is required.
"""

import pytest
from hermes_cli import model_switch


def _make_provider(slug, name=None, models=None, *, is_current=False,
                   is_user_defined=False, source="built-in", api_url=None):
    """Build a dict shaped like ``list_authenticated_providers`` output."""
    entry = {
        "slug": slug,
        "name": name or slug.title(),
        "is_current": is_current,
        "is_user_defined": is_user_defined,
        "models": list(models or []),
        "total_models": len(models or []),
        "source": source,
    }
    if api_url is not None:
        entry["api_url"] = api_url
    return entry


def test_openrouter_models_replaced_with_live_catalog(monkeypatch):
    """OpenRouter row's ``models`` should come from fetch_openrouter_models."""
    base = [
        _make_provider("openrouter", models=["openai/gpt-stale", "old/model"]),
    ]
    live = [("openai/gpt-5.4", "recommended"), ("moonshotai/kimi-k2.6", "")]

    monkeypatch.setattr(model_switch, "list_authenticated_providers",
                        lambda **kw: list(base))
    monkeypatch.setattr("hermes_cli.models.fetch_openrouter_models",
                        lambda *a, **kw: list(live))

    result = model_switch.list_picker_providers(max_models=50)

    assert len(result) == 1
    openrouter = result[0]
    assert openrouter["slug"] == "openrouter"
    assert openrouter["models"] == ["openai/gpt-5.4", "moonshotai/kimi-k2.6"]
    assert openrouter["total_models"] == 2


def test_openrouter_falls_back_to_base_models_on_fetch_failure(monkeypatch):
    """If the live catalog fetch raises, keep whatever base provided."""
    fallback_models = ["openai/gpt-5.4", "moonshotai/kimi-k2.6"]
    base = [_make_provider("openrouter", models=fallback_models)]

    def _raise(*_a, **_kw):
        raise RuntimeError("network down")

    monkeypatch.setattr(model_switch, "list_authenticated_providers",
                        lambda **kw: list(base))
    monkeypatch.setattr("hermes_cli.models.fetch_openrouter_models", _raise)

    result = model_switch.list_picker_providers(max_models=50)

    assert len(result) == 1
    assert result[0]["models"] == fallback_models


def test_openrouter_empty_live_catalog_drops_row(monkeypatch):
    """If the live catalog returns nothing for OpenRouter, drop the row."""
    base = [_make_provider("openrouter", models=["something/stale"])]

    monkeypatch.setattr(model_switch, "list_authenticated_providers",
                        lambda **kw: list(base))
    monkeypatch.setattr("hermes_cli.models.fetch_openrouter_models",
                        lambda *a, **kw: [])

    result = model_switch.list_picker_providers(max_models=50)

    assert result == []


def test_non_openrouter_rows_passed_through_unchanged(monkeypatch):
    """Non-OpenRouter providers keep their curated ``models`` as-is."""
    base = [
        _make_provider("anthropic", models=["claude-sonnet-4-6", "claude-opus-4-7"]),
        _make_provider("gemini", models=["gemini-3-flash-preview"]),
    ]

    monkeypatch.setattr(model_switch, "list_authenticated_providers",
                        lambda **kw: list(base))
    # fetch_openrouter_models must not be consulted when there's no openrouter row
    monkeypatch.setattr("hermes_cli.models.fetch_openrouter_models",
                        lambda *a, **kw: pytest.fail("should not be called"))

    result = model_switch.list_picker_providers(max_models=50)

    assert [p["slug"] for p in result] == ["anthropic", "gemini"]
    assert result[0]["models"] == ["claude-sonnet-4-6", "claude-opus-4-7"]
    assert result[1]["models"] == ["gemini-3-flash-preview"]


def test_empty_models_row_dropped(monkeypatch):
    """Built-in provider with an empty ``models`` list is dropped."""
    base = [
        _make_provider("anthropic", models=[]),  # drop
        _make_provider("openrouter", models=["anything"]),  # replaced by live
    ]

    monkeypatch.setattr(model_switch, "list_authenticated_providers",
                        lambda **kw: list(base))
    monkeypatch.setattr("hermes_cli.models.fetch_openrouter_models",
                        lambda *a, **kw: [("openai/gpt-5.4", "recommended")])

    result = model_switch.list_picker_providers(max_models=50)

    assert [p["slug"] for p in result] == ["openrouter"]


def test_custom_endpoint_with_api_url_kept_when_models_empty(monkeypatch):
    """User-defined endpoints with an ``api_url`` survive even if models empty.

    Rationale: custom endpoints may accept any model id the user types --
    the picker still shows the row so the user can enter one manually.
    """
    base = [
        _make_provider("local-ollama", is_user_defined=True,
                       api_url="http://localhost:11434/v1", models=[],
                       source="user-config"),
    ]

    monkeypatch.setattr(model_switch, "list_authenticated_providers",
                        lambda **kw: list(base))
    monkeypatch.setattr("hermes_cli.models.fetch_openrouter_models",
                        lambda *a, **kw: [])

    result = model_switch.list_picker_providers(max_models=50)

    assert len(result) == 1
    assert result[0]["slug"] == "local-ollama"
    assert result[0]["models"] == []


def test_user_defined_without_api_url_and_empty_models_dropped(monkeypatch):
    """An is_user_defined row WITHOUT api_url and no models is still dropped.

    The exemption is specifically for custom endpoints that can accept
    arbitrary model ids; without an api_url there's nothing to point at.
    """
    base = [
        _make_provider("orphan", is_user_defined=True, api_url=None, models=[]),
    ]

    monkeypatch.setattr(model_switch, "list_authenticated_providers",
                        lambda **kw: list(base))
    monkeypatch.setattr("hermes_cli.models.fetch_openrouter_models",
                        lambda *a, **kw: [])

    result = model_switch.list_picker_providers(max_models=50)

    assert result == []


def test_max_models_caps_openrouter_live_output(monkeypatch):
    """``max_models`` caps how many OpenRouter IDs land in the row."""
    live = [(f"vendor/model-{i}", "") for i in range(20)]
    base = [_make_provider("openrouter", models=["placeholder"])]

    monkeypatch.setattr(model_switch, "list_authenticated_providers",
                        lambda **kw: list(base))
    monkeypatch.setattr("hermes_cli.models.fetch_openrouter_models",
                        lambda *a, **kw: list(live))

    result = model_switch.list_picker_providers(max_models=5)

    assert len(result) == 1
    assert len(result[0]["models"]) == 5
    assert result[0]["models"] == [mid for mid, _ in live[:5]]
    # total_models reflects the full live catalog, not the capped slice.
    assert result[0]["total_models"] == 20


def test_passthrough_kwargs_to_base(monkeypatch):
    """All kwargs must be forwarded to ``list_authenticated_providers`` unchanged.

    The gateway /model picker passes ``current_base_url`` and ``current_model``
    so custom endpoint grouping can mark the current row. Dropping those kwargs
    regressed Telegram/Discord into the text-list fallback.
    """
    captured = {}

    def _capture(**kwargs):
        captured.update(kwargs)
        return []

    monkeypatch.setattr(model_switch, "list_authenticated_providers", _capture)
    monkeypatch.setattr("hermes_cli.models.fetch_openrouter_models",
                        lambda *a, **kw: [])

    model_switch.list_picker_providers(
        current_provider="openrouter",
        current_base_url="http://x",
        current_model="openai/gpt-5.4",
        user_providers={"foo": {"api": "http://x"}},
        custom_providers=[{"name": "bar", "base_url": "http://y"}],
        max_models=12,
    )

    assert captured["current_provider"] == "openrouter"
    assert captured["current_base_url"] == "http://x"
    assert captured["current_model"] == "openai/gpt-5.4"
    assert captured["user_providers"] == {"foo": {"api": "http://x"}}
    assert captured["custom_providers"] == [{"name": "bar", "base_url": "http://y"}]
    assert captured["max_models"] == 12


def test_current_custom_endpoint_passthrough_marks_current_row(monkeypatch):
    """Interactive picker should preserve current custom endpoint semantics."""
    monkeypatch.setattr("agent.models_dev.fetch_models_dev", lambda: {})
    monkeypatch.setattr("agent.models_dev.PROVIDER_TO_MODELS_DEV", {})
    monkeypatch.setattr("hermes_cli.providers.HERMES_OVERLAYS", {})
    monkeypatch.setattr("hermes_cli.models.fetch_openrouter_models",
                        lambda *a, **kw: [])

    result = model_switch.list_picker_providers(
        current_provider="custom:ollama",
        current_base_url="http://localhost:11434/v1",
        current_model="glm-5.1",
        user_providers={},
        custom_providers=[
            {
                "name": "Ollama — GLM 5.1",
                "base_url": "http://localhost:11434/v1",
                "api_key": "ollama",
                "model": "glm-5.1",
            },
            {
                "name": "Ollama — Qwen3",
                "base_url": "http://localhost:11434/v1",
                "api_key": "ollama",
                "model": "qwen3",
            },
        ],
        max_models=50,
    )

    custom_rows = [p for p in result if p.get("is_user_defined")]
    assert len(custom_rows) == 1
    row = custom_rows[0]
    assert row["slug"] == "custom:ollama"
    assert row["is_current"] is True
    assert row["models"] == ["glm-5.1", "qwen3"]
