"""Tests for stale-MCP-registry detection in load_mcp_registry (issue #89).

A user's ``~/.aiswmm/mcp.json`` may have been generated by an older checkout
that pre-dates one or more MCP servers shipped by the current code. Without
detection, the agent dispatches tool calls to MCP servers that aren't
registered and fails with ``unknown MCP server: <name>``. The fix: detect
the mismatch, warn loudly, and fall back to in-process discovery so the
workflow keeps moving until the user re-runs ``aiswmm setup``.
"""

from __future__ import annotations

import json
import os
from pathlib import Path

import pytest

from agentic_swmm.runtime import registry


def _write_partial_registry(path: Path, *, names: list[str]) -> None:
    payload = {
        "mcp_servers": [
            {"name": name, "enabled": True, "exists": True}
            for name in names
        ]
    }
    path.parent.mkdir(parents=True, exist_ok=True)
    path.write_text(json.dumps(payload, indent=2), encoding="utf-8")


@pytest.fixture
def isolated_config_dir(tmp_path, monkeypatch):
    monkeypatch.setenv("AISWMM_CONFIG_DIR", str(tmp_path))
    yield tmp_path


def test_full_registry_loads_unchanged(isolated_config_dir, capsys):
    """A fully-populated mcp.json returns its records verbatim, no warning."""
    mcp_path = isolated_config_dir / "mcp.json"
    _write_partial_registry(mcp_path, names=list(registry.MCP_SERVERS))

    records = registry.load_mcp_registry()

    assert {r["name"] for r in records} == set(registry.MCP_SERVERS)
    assert "warn:" not in capsys.readouterr().err


def test_missing_servers_triggers_warning_and_fallback(isolated_config_dir, capsys):
    """A stale mcp.json with fewer entries falls back to in-process discovery."""
    mcp_path = isolated_config_dir / "mcp.json"
    stale = [name for name in registry.MCP_SERVERS if name not in {
        "swmm-experiment-audit",
        "swmm-modeling-memory",
        "swmm-uncertainty",
    }]
    _write_partial_registry(mcp_path, names=stale)

    records = registry.load_mcp_registry()

    discovered_names = {r["name"] for r in records}
    assert discovered_names == set(registry.MCP_SERVERS), (
        "fallback must return full discovery set, not the stale entries"
    )
    stderr = capsys.readouterr().err
    assert "warn:" in stderr
    assert "swmm-experiment-audit" in stderr
    assert "swmm-modeling-memory" in stderr
    assert "swmm-uncertainty" in stderr
    assert "aiswmm setup" in stderr


def test_no_registry_file_returns_discovery(isolated_config_dir, capsys):
    """Absent mcp.json returns discovery (existing behaviour, no warning)."""
    records = registry.load_mcp_registry()
    assert {r["name"] for r in records} == set(registry.MCP_SERVERS)
    assert "warn:" not in capsys.readouterr().err


def test_malformed_registry_falls_back_with_warning(isolated_config_dir, capsys):
    """A registry with a non-list payload falls back to discovery."""
    mcp_path = isolated_config_dir / "mcp.json"
    mcp_path.parent.mkdir(parents=True, exist_ok=True)
    mcp_path.write_text(json.dumps({"mcp_servers": "not-a-list"}), encoding="utf-8")

    records = registry.load_mcp_registry()

    assert {r["name"] for r in records} == set(registry.MCP_SERVERS)
    assert "warn:" in capsys.readouterr().err
