"""Regression tests for #1527/#1530 LM Studio base_url ownership.

When a local OpenAI-compatible endpoint is configured as LM Studio, model
discovery must trust the configured provider before guessing from the URL host.
LAN IPs, Tailscale names, and reverse proxies do not contain "lmstudio" in the
hostname, but the config block already says which provider owns that base_url.
"""

from __future__ import annotations

import json
import socket
import urllib.request

import pytest

import api.config as config
import api.profiles as profiles


_API_KEY_ENV_VARS = (
    "ANTHROPIC_API_KEY",
    "OPENAI_API_KEY",
    "OPENROUTER_API_KEY",
    "GOOGLE_API_KEY",
    "GEMINI_API_KEY",
    "GLM_API_KEY",
    "KIMI_API_KEY",
    "DEEPSEEK_API_KEY",
    "OPENCODE_ZEN_API_KEY",
    "OPENCODE_GO_API_KEY",
    "MINIMAX_API_KEY",
    "MINIMAX_CN_API_KEY",
    "XAI_API_KEY",
    "MISTRAL_API_KEY",
    "LM_API_KEY",
    "LMSTUDIO_API_KEY",
    "OLLAMA_API_KEY",
    "LOCAL_API_KEY",
    "API_KEY",
)


class _ModelsResponse:
    def __init__(self, model_ids: list[str]):
        self._model_ids = model_ids

    def __enter__(self):
        return self

    def __exit__(self, *_args):
        return None

    def read(self) -> bytes:
        return json.dumps({"data": [{"id": mid} for mid in self._model_ids]}).encode()


@pytest.fixture(autouse=True)
def _isolate_config(monkeypatch, tmp_path):
    old_cfg = dict(config.cfg)
    old_mtime = config._cfg_mtime
    monkeypatch.setattr(profiles, "get_active_hermes_home", lambda: tmp_path)
    for var in _API_KEY_ENV_VARS:
        monkeypatch.delenv(var, raising=False)
    config.invalidate_models_cache()
    yield
    config.cfg.clear()
    config.cfg.update(old_cfg)
    config._cfg_mtime = old_mtime
    config.invalidate_models_cache()


def _write_config(tmp_path, monkeypatch, text: str) -> None:
    cfgfile = tmp_path / "config.yaml"
    cfgfile.write_text(text, encoding="utf-8")
    monkeypatch.setattr(config, "_get_config_path", lambda: cfgfile)
    config.reload_config()
    config.invalidate_models_cache()


def _mock_model_discovery(monkeypatch, model_ids: list[str], resolved_ip: str) -> None:
    monkeypatch.setattr(
        urllib.request,
        "urlopen",
        lambda *_args, **_kwargs: _ModelsResponse(model_ids),
    )
    monkeypatch.setattr(
        socket,
        "getaddrinfo",
        lambda *_args, **_kwargs: [
            (socket.AF_INET, socket.SOCK_STREAM, 6, "", (resolved_ip, 0))
        ],
    )


def _groups_by_id() -> dict[str, dict]:
    return {
        group["provider_id"]: group
        for group in config.get_available_models()["groups"]
    }


@pytest.mark.parametrize(
    ("base_url", "resolved_ip"),
    [
        ("http://192.168.1.22:1234/v1", "192.168.1.22"),
        ("http://my-mac.tailnet.example:1234/v1", "192.168.1.22"),
        ("https://lm.internal.example.com/v1", "192.168.1.22"),
    ],
)
def test_lmstudio_configured_base_url_keeps_discovered_models(
    tmp_path,
    monkeypatch,
    base_url: str,
    resolved_ip: str,
):
    _write_config(
        tmp_path,
        monkeypatch,
        f"""
model:
  provider: lmstudio
  default: qwen3.6-35b-a3b@q6_k
  base_url: {base_url}
providers:
  lmstudio:
    api_key: local-key
""",
    )
    _mock_model_discovery(
        monkeypatch,
        ["qwen3.6-35b-a3b@q6_k", "second-lmstudio-model"],
        resolved_ip,
    )

    groups = _groups_by_id()
    assert "custom" not in groups
    assert "lmstudio" in groups
    model_ids = {model["id"] for model in groups["lmstudio"]["models"]}
    bare_model_ids = {mid.removeprefix("@lmstudio:") for mid in model_ids}
    assert {"qwen3.6-35b-a3b@q6_k", "second-lmstudio-model"} <= bare_model_ids


def test_custom_configured_base_url_is_not_reclassified_as_ollama(tmp_path, monkeypatch):
    _write_config(
        tmp_path,
        monkeypatch,
        """
model:
  provider: custom
  default: custom-model
  base_url: http://localhost:4000/v1
providers:
  custom:
    api_key: local-key
""",
    )
    _mock_model_discovery(monkeypatch, ["custom-model", "custom-extra"], "127.0.0.1")

    groups = _groups_by_id()
    assert "ollama" not in groups
    assert "custom" in groups
    model_ids = {model["id"] for model in groups["custom"]["models"]}
    assert {"custom-model", "custom-extra"} <= model_ids


def test_lmstudio_session_model_resolves_to_configured_base_url(tmp_path, monkeypatch):
    _write_config(
        tmp_path,
        monkeypatch,
        """
model:
  provider: lmstudio
  default: qwen3.6-35b-a3b@q6_k
  base_url: http://192.168.1.22:1234/v1
providers:
  lmstudio:
    api_key: local-key
""",
    )

    model, provider, base_url = config.resolve_model_provider(
        "qwen3.6-35b-a3b@q6_k"
    )

    assert model == "qwen3.6-35b-a3b@q6_k"
    assert provider == "lmstudio"
    assert base_url == "http://192.168.1.22:1234/v1"
