"""Tests for the MCP server module."""

import json
import os
import tempfile
from pathlib import Path
from unittest.mock import MagicMock, PropertyMock, patch

import pytest

from augur_mcp.server import (
    CREDS_FILENAME,
    _format_error,
    _init_clients,
    _load_all_creds,
    _parse_site_cred,
    _read_creds_json,
    augur_create,
    augur_delete,
    augur_discover,
    augur_get,
    augur_list,
    augur_sites,
    augur_update,
    get_api,
    init_catalog,
    reset_state,
    set_api,
)


@pytest.fixture()
def _catalog_loaded(endpoints_dir):
    """Load the endpoints catalog for tests."""
    init_catalog(endpoints_dir)


@pytest.fixture()
def _mock_api_configured(mock_api):
    """Configure a mock API and set it."""
    set_api(mock_api)
    return mock_api


# --- init_catalog / reset_state ---


class TestCatalogInit:
    def test_init_loads_endpoints(self, endpoints_dir):
        init_catalog(endpoints_dir)
        result = json.loads(augur_discover())
        assert "services" in result
        assert len(result["services"]) > 0

    def test_reset_clears_state(self, endpoints_dir):
        init_catalog(endpoints_dir)
        reset_state()
        result = json.loads(augur_discover())
        assert result["services"] == []


# --- _parse_site_cred ---


class TestParseSiteCred:
    def test_jwt_cred_with_site_id_in_data(self):
        result = _parse_site_cred({"siteId": "my-site", "jwt": "my-token"})
        assert result == {"site_id": "my-site", "jwt": "my-token"}

    def test_explicit_site_id_param(self):
        result = _parse_site_cred({"jwt": "tok"}, site_id="from-key")
        assert result == {"site_id": "from-key", "jwt": "tok"}

    def test_explicit_site_id_overrides_data(self):
        result = _parse_site_cred(
            {"siteId": "in-data", "jwt": "tok"}, site_id="from-key"
        )
        assert result["site_id"] == "from-key"

    def test_no_site_id_anywhere(self):
        assert _parse_site_cred({"jwt": "token"}) is None

    def test_empty_site_id(self):
        assert _parse_site_cred({"siteId": "", "jwt": "token"}) is None

    def test_no_jwt(self):
        assert _parse_site_cred({"siteId": "my-site"}) is None

    def test_empty_jwt(self):
        assert _parse_site_cred({"siteId": "s", "jwt": ""}) is None

    def test_extra_fields_ignored(self):
        result = _parse_site_cred({"siteId": "s", "jwt": "t", "extra": "x"})
        assert result == {"site_id": "s", "jwt": "t"}


# --- _read_creds_json ---


class TestReadCredsJson:
    def test_single_site(self):
        with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
            json.dump({"siteId": "my-site", "jwt": "my-token"}, f)
            f.flush()
            creds = _read_creds_json(Path(f.name))
            assert len(creds) == 1
            assert creds[0] == {"site_id": "my-site", "jwt": "my-token"}
            os.unlink(f.name)

    def test_multi_site(self):
        data = {
            "site-a": {"jwt": "tok-a"},
            "site-b": {"jwt": "tok-b"},
        }
        with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
            json.dump(data, f)
            f.flush()
            creds = _read_creds_json(Path(f.name))
            assert len(creds) == 2
            ids = {c["site_id"] for c in creds}
            assert ids == {"site-a", "site-b"}
            os.unlink(f.name)

    def test_multi_site_skips_invalid(self):
        data = {
            "good": {"jwt": "tok"},
            "bad": {},
            "non-dict": "skip me",
        }
        with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
            json.dump(data, f)
            f.flush()
            creds = _read_creds_json(Path(f.name))
            assert len(creds) == 1
            assert creds[0]["site_id"] == "good"
            os.unlink(f.name)

    def test_missing_file(self):
        assert _read_creds_json(Path("/nonexistent/file.json")) == []

    def test_incomplete_file(self):
        with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
            json.dump({"siteId": "my-site"}, f)
            f.flush()
            assert _read_creds_json(Path(f.name)) == []
            os.unlink(f.name)

    def test_empty_values(self):
        with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
            json.dump({"siteId": "", "jwt": ""}, f)
            f.flush()
            assert _read_creds_json(Path(f.name)) == []
            os.unlink(f.name)

    def test_non_dict_json(self):
        with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
            json.dump([1, 2, 3], f)
            f.flush()
            assert _read_creds_json(Path(f.name)) == []
            os.unlink(f.name)


# --- _load_all_creds ---


class TestLoadAllCreds:
    def test_explicit_path(self):
        with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
            json.dump({"siteId": "my-site", "jwt": "my-token"}, f)
            f.flush()
            creds = _load_all_creds(Path(f.name))
            assert len(creds) == 1
            assert creds[0]["site_id"] == "my-site"
            os.unlink(f.name)

    def test_explicit_path_missing(self):
        assert _load_all_creds(Path("/nonexistent/file.json")) == []

    def test_env_var_path(self):
        with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
            json.dump({"siteId": "env-site", "jwt": "env-token"}, f)
            f.flush()
            with patch.dict(os.environ, {"AUGUR_CREDS_FILE": f.name}, clear=False):
                creds = _load_all_creds()
                assert len(creds) == 1
                assert creds[0]["site_id"] == "env-site"
            os.unlink(f.name)

    def test_env_var_missing_file(self):
        with patch.dict(
            os.environ, {"AUGUR_CREDS_FILE": "/nonexistent/creds.json"}, clear=False
        ):
            assert _load_all_creds() == []

    def test_project_creds(self, tmp_path):
        """Loads from <cwd>/.simpleapps/augur-api.json."""
        simpleapps = tmp_path / ".simpleapps"
        simpleapps.mkdir()
        (simpleapps / CREDS_FILENAME).write_text(
            json.dumps({"siteId": "proj-site", "jwt": "proj-tok"})
        )
        with patch.dict(os.environ, {}, clear=True), patch(
            "augur_mcp.server.Path.cwd", return_value=tmp_path
        ), patch(
            "augur_mcp.server._read_creds_json",
            wraps=_read_creds_json,
        ):
            creds = _load_all_creds()
            assert any(c["site_id"] == "proj-site" for c in creds)

    def test_global_fallback(self):
        """Falls back to DEFAULT_CREDS_FILE when nothing else matches."""
        with patch.dict(os.environ, {}, clear=True), patch(
            "augur_mcp.server.Path.cwd", return_value=Path("/nonexistent")
        ), patch(
            "augur_mcp.server._read_creds_json", return_value=[]
        ) as mock_read:
            result = _load_all_creds()
            assert result == []
            from augur_mcp.server import DEFAULT_CREDS_FILE

            # Should have been called for both project and global paths
            assert mock_read.call_count == 2
            mock_read.assert_any_call(DEFAULT_CREDS_FILE)

    def test_project_overrides_global(self, tmp_path):
        """Project creds take precedence over global for same site_id."""
        proj_dir = tmp_path / ".simpleapps"
        proj_dir.mkdir()
        (proj_dir / CREDS_FILENAME).write_text(
            json.dumps({"siteId": "dupe", "jwt": "proj-token"})
        )
        global_dir = tmp_path / "global" / ".simpleapps"
        global_dir.mkdir(parents=True)
        global_file = global_dir / CREDS_FILENAME
        global_file.write_text(
            json.dumps({"siteId": "dupe", "jwt": "global-token"})
        )
        with patch.dict(os.environ, {}, clear=True), patch(
            "augur_mcp.server.Path.cwd", return_value=tmp_path
        ), patch("augur_mcp.server.DEFAULT_CREDS_FILE", global_file):
            creds = _load_all_creds()
            dupes = [c for c in creds if c["site_id"] == "dupe"]
            assert len(dupes) == 1
            assert dupes[0]["jwt"] == "proj-token"

    def test_merge_project_and_global(self, tmp_path):
        """Different sites from project and global are merged."""
        proj_dir = tmp_path / ".simpleapps"
        proj_dir.mkdir()
        (proj_dir / CREDS_FILENAME).write_text(
            json.dumps({"siteId": "proj-site", "jwt": "proj-tok"})
        )
        global_dir = tmp_path / "global" / ".simpleapps"
        global_dir.mkdir(parents=True)
        global_file = global_dir / CREDS_FILENAME
        global_file.write_text(
            json.dumps({"siteId": "global-site", "jwt": "global-tok"})
        )
        with patch.dict(os.environ, {}, clear=True), patch(
            "augur_mcp.server.Path.cwd", return_value=tmp_path
        ), patch("augur_mcp.server.DEFAULT_CREDS_FILE", global_file):
            creds = _load_all_creds()
            ids = {c["site_id"] for c in creds}
            assert ids == {"proj-site", "global-site"}

    def test_explicit_arg_overrides_env(self):
        with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
            json.dump({"siteId": "arg-site", "jwt": "arg-token"}, f)
            f.flush()
            with patch.dict(
                os.environ, {"AUGUR_CREDS_FILE": "/other/path.json"}, clear=False
            ):
                creds = _load_all_creds(Path(f.name))
                assert len(creds) == 1
                assert creds[0]["site_id"] == "arg-site"
            os.unlink(f.name)

    def test_default_path_constant(self):
        from augur_mcp.server import DEFAULT_CREDS_FILE

        assert DEFAULT_CREDS_FILE.name == "augur-api.json"
        assert ".simpleapps" in str(DEFAULT_CREDS_FILE)

    def test_creds_filename_constant(self):
        assert CREDS_FILENAME == "augur-api.json"


# --- _init_clients ---


class TestInitClients:
    def test_from_env_vars(self):
        with patch.dict(
            os.environ,
            {"AUGUR_TOKEN": "env-tok", "AUGUR_SITE_ID": "env-site"},
            clear=True,
        ), patch("augur_mcp.server._load_all_creds", return_value=[]):
            _init_clients()
            from augur_mcp.server import _clients

            assert "env-site" in _clients
            assert _clients["env-site"].config.token == "env-tok"

    def test_from_file_creds(self, tmp_path):
        creds_file = tmp_path / "creds.json"
        creds_file.write_text(json.dumps({"siteId": "file-site", "jwt": "file-tok"}))
        with patch.dict(os.environ, {}, clear=True):
            _init_clients(creds_path=creds_file)
            from augur_mcp.server import _clients

            assert "file-site" in _clients

    def test_env_overrides_file_same_site(self, tmp_path):
        creds_file = tmp_path / "creds.json"
        creds_file.write_text(
            json.dumps({"siteId": "my-site", "jwt": "file-tok"})
        )
        with patch.dict(
            os.environ,
            {"AUGUR_TOKEN": "env-tok", "AUGUR_SITE_ID": "my-site"},
            clear=True,
        ):
            _init_clients(creds_path=creds_file)
            from augur_mcp.server import _clients

            assert _clients["my-site"].config.token == "env-tok"

    def test_env_plus_file_different_sites(self, tmp_path):
        creds_file = tmp_path / "creds.json"
        creds_file.write_text(
            json.dumps({"siteId": "file-site", "jwt": "file-tok"})
        )
        with patch.dict(
            os.environ,
            {"AUGUR_TOKEN": "env-tok", "AUGUR_SITE_ID": "env-site"},
            clear=True,
        ):
            _init_clients(creds_path=creds_file)
            from augur_mcp.server import _clients

            assert "env-site" in _clients
            assert "file-site" in _clients

    def test_partial_env_token_merges_with_file(self, tmp_path):
        """AUGUR_TOKEN without AUGUR_SITE_ID merges with first file cred."""
        creds_file = tmp_path / "creds.json"
        creds_file.write_text(
            json.dumps({"siteId": "file-site", "jwt": "file-tok"})
        )
        with patch.dict(os.environ, {"AUGUR_TOKEN": "env-tok"}, clear=True):
            _init_clients(creds_path=creds_file)
            from augur_mcp.server import _clients

            assert "file-site" in _clients
            assert _clients["file-site"].config.token == "env-tok"

    def test_partial_env_site_id_merges_with_file(self, tmp_path):
        """AUGUR_SITE_ID without AUGUR_TOKEN merges with first file cred."""
        creds_file = tmp_path / "creds.json"
        creds_file.write_text(
            json.dumps({"siteId": "file-site", "jwt": "file-tok"})
        )
        with patch.dict(os.environ, {"AUGUR_SITE_ID": "override-site"}, clear=True):
            _init_clients(creds_path=creds_file)
            from augur_mcp.server import _clients

            assert "override-site" in _clients
            assert _clients["override-site"].config.token == "file-tok"

    def test_partial_env_no_file_creds(self):
        """Partial env with no file creds results in no clients."""
        with patch.dict(
            os.environ, {"AUGUR_TOKEN": "tok"}, clear=True
        ), patch("augur_mcp.server._load_all_creds", return_value=[]):
            _init_clients()
            from augur_mcp.server import _clients

            assert len(_clients) == 0

    def test_sets_initialized_flag(self):
        with patch.dict(os.environ, {}, clear=True), patch(
            "augur_mcp.server._load_all_creds", return_value=[]
        ):
            _init_clients()
            from augur_mcp.server import _initialized

            assert _initialized is True

    def test_multi_site_file(self, tmp_path):
        data = {
            "site-a": {"jwt": "tok-a"},
            "site-b": {"jwt": "tok-b"},
        }
        creds_file = tmp_path / "creds.json"
        creds_file.write_text(json.dumps(data))
        with patch.dict(os.environ, {}, clear=True):
            _init_clients(creds_path=creds_file)
            from augur_mcp.server import _clients

            assert "site-a" in _clients
            assert "site-b" in _clients


# --- get_api / set_api ---


class TestApiManagement:
    def test_get_api_no_creds(self):
        with patch.dict(os.environ, {}, clear=True), patch(
            "augur_mcp.server._load_all_creds", return_value=[]
        ):
            with pytest.raises(ValueError, match="No Augur credentials found"):
                get_api()

    def test_get_api_error_mentions_all_options(self):
        """Error message mentions all 4 auth resolution options."""
        with patch.dict(os.environ, {}, clear=True), patch(
            "augur_mcp.server._load_all_creds", return_value=[]
        ):
            with pytest.raises(ValueError, match="AUGUR_CREDS_FILE"):
                get_api()
            reset_state()
            with pytest.raises(ValueError, match="simpleapps/augur-api.json"):
                get_api()

    def test_get_api_from_env(self):
        with patch.dict(
            os.environ,
            {"AUGUR_TOKEN": "test-token", "AUGUR_SITE_ID": "test-site"},
            clear=True,
        ), patch("augur_mcp.server._load_all_creds", return_value=[]):
            api = get_api()
            assert api.config.token == "test-token"
            assert api.config.site_id == "test-site"

    def test_get_api_from_file(self):
        with patch.dict(os.environ, {}, clear=True), patch(
            "augur_mcp.server._load_all_creds",
            return_value=[{"site_id": "f-site", "jwt": "f-tok"}],
        ):
            api = get_api()
            assert api.config.token == "f-tok"
            assert api.config.site_id == "f-site"

    def test_get_api_caches_client(self):
        with patch.dict(
            os.environ,
            {"AUGUR_TOKEN": "test-token", "AUGUR_SITE_ID": "test-site"},
            clear=True,
        ), patch("augur_mcp.server._load_all_creds", return_value=[]):
            api1 = get_api()
            api2 = get_api()
            assert api1 is api2

    def test_get_api_single_site_no_param(self, mock_api):
        set_api(mock_api)
        assert get_api() is mock_api

    def test_get_api_multi_site_requires_param(self):
        set_api(MagicMock(), site="site-a")
        set_api(MagicMock(), site="site-b")
        with pytest.raises(ValueError, match="Multiple sites configured"):
            get_api()

    def test_get_api_multi_site_with_param(self):
        mock_a = MagicMock()
        mock_b = MagicMock()
        set_api(mock_a, site="site-a")
        set_api(mock_b, site="site-b")
        assert get_api("site-a") is mock_a
        assert get_api("site-b") is mock_b

    def test_get_api_unknown_site(self, mock_api):
        set_api(mock_api)
        with pytest.raises(ValueError, match="Unknown site"):
            get_api("nonexistent")

    def test_set_api_overrides(self, mock_api):
        set_api(mock_api)
        assert get_api() is mock_api

    def test_set_api_with_explicit_site(self, mock_api):
        set_api(mock_api, site="custom-key")
        assert get_api("custom-key") is mock_api

    def test_set_api_derives_key_from_config(self, mock_api):
        """set_api uses api.config.site_id as key when site not provided."""
        mock_api.config.site_id = "derived-site"
        set_api(mock_api)
        assert get_api("derived-site") is mock_api

    def test_set_api_fallback_key(self):
        """Falls back to 'default' when no site and no config.site_id."""
        api = MagicMock(spec=[])  # no config attribute
        set_api(api)
        assert get_api("default") is api


# --- _format_error ---


class TestFormatError:
    def test_authentication_error(self):
        from augur_api.core.errors import AuthenticationError

        err = AuthenticationError("bad token", "agr-site", "/settings")
        result = json.loads(_format_error(err))
        assert result["error"] == "Authentication failed"

    def test_not_found_error(self):
        from augur_api.core.errors import NotFoundError

        err = NotFoundError("not found", "agr-site", "/settings/99")
        result = json.loads(_format_error(err))
        assert result["error"] == "Not found"

    def test_rate_limit_error(self):
        from augur_api.core.errors import RateLimitError

        err = RateLimitError("too many requests", "agr-site", "/settings")
        result = json.loads(_format_error(err))
        assert result["error"] == "Rate limited"

    def test_validation_error(self):
        from augur_api.core.errors import ValidationError

        err = ValidationError(
            "bad input", "agr-site", "/settings", [{"msg": "invalid"}]
        )
        result = json.loads(_format_error(err))
        assert result["error"] == "Validation error"

    def test_generic_augur_error(self):
        from augur_api.core.errors import AugurError

        err = AugurError("something broke", "API_ERROR", 500, "agr-site", "/settings")
        result = json.loads(_format_error(err))
        assert result["error"] == "API error"

    def test_value_error(self):
        err = ValueError("bad value")
        result = json.loads(_format_error(err))
        assert result["error"] == "Invalid request"

    def test_unexpected_error(self):
        err = RuntimeError("unexpected")
        result = json.loads(_format_error(err))
        assert result["error"] == "Unexpected error"


# --- augur_sites ---


class TestAugurSites:
    def test_no_sites(self):
        with patch.dict(os.environ, {}, clear=True), patch(
            "augur_mcp.server._load_all_creds", return_value=[]
        ):
            result = json.loads(augur_sites())
            assert result["sites"] == []

    def test_single_site(self, mock_api):
        set_api(mock_api, site="my-site")
        result = json.loads(augur_sites())
        sites = result["sites"]
        assert len(sites) == 1
        assert sites[0]["site_id"] == "my-site"
        assert sites[0]["default"] is True

    def test_multiple_sites(self):
        set_api(MagicMock(), site="site-a")
        set_api(MagicMock(), site="site-b")
        result = json.loads(augur_sites())
        sites = result["sites"]
        assert len(sites) == 2
        assert not any(s["default"] for s in sites)

    def test_init_exception_handled(self):
        """augur_sites handles _init_clients exceptions gracefully."""
        with patch(
            "augur_mcp.server._init_clients", side_effect=RuntimeError("boom")
        ):
            result = json.loads(augur_sites())
            assert "sites" in result


# --- augur_discover ---


class TestAugurDiscover:
    @pytest.mark.usefixtures("_catalog_loaded")
    def test_list_all_services(self):
        result = json.loads(augur_discover())
        assert "services" in result
        services = result["services"]
        names = [s["name"] for s in services]
        assert "agr-site" in names
        assert "agr-info" in names

    @pytest.mark.usefixtures("_catalog_loaded")
    def test_service_has_metadata(self):
        result = json.loads(augur_discover())
        svc = result["services"][0]
        assert "name" in svc
        assert "endpoints" in svc
        assert "resources" in svc

    @pytest.mark.usefixtures("_catalog_loaded")
    def test_specific_service(self):
        result = json.loads(augur_discover(service="agr-site"))
        assert result["service"] == "agr-site"
        assert "endpoints" in result
        assert len(result["endpoints"]) > 0

    @pytest.mark.usefixtures("_catalog_loaded")
    def test_specific_service_endpoint_structure(self):
        result = json.loads(augur_discover(service="agr-site"))
        ep = result["endpoints"][0]
        assert "resource" in ep
        assert "methods" in ep

    @pytest.mark.usefixtures("_catalog_loaded")
    def test_unknown_service(self):
        result = json.loads(augur_discover(service="nonexistent"))
        assert "error" in result
        assert "available" in result

    def test_empty_catalog(self):
        result = json.loads(augur_discover())
        assert result["services"] == []


# --- augur_list ---


class TestAugurList:
    @pytest.mark.usefixtures("_catalog_loaded")
    def test_list_success(self, mock_api):
        set_api(mock_api)
        mock_response = MagicMock()
        mock_response.model_dump.return_value = {
            "data": [{"id": 1}],
            "count": 1,
            "total": 10,
        }
        mock_resource = MagicMock()
        mock_resource.list.return_value = mock_response
        mock_svc = MagicMock()
        mock_svc.settings = mock_resource
        type(mock_api).agr_site = PropertyMock(return_value=mock_svc)

        result = json.loads(augur_list(service="agr-site", resource="settings"))
        assert result["count"] == 1
        assert result["total"] == 10

    @pytest.mark.usefixtures("_catalog_loaded")
    def test_list_with_params(self, mock_api):
        set_api(mock_api)
        mock_response = MagicMock()
        mock_response.model_dump.return_value = {"data": [], "count": 0, "total": 0}
        mock_resource = MagicMock()
        mock_resource.list.return_value = mock_response
        mock_svc = MagicMock()
        mock_svc.settings = mock_resource
        type(mock_api).agr_site = PropertyMock(return_value=mock_svc)

        json.loads(
            augur_list(
                service="agr-site", resource="settings", params='{"limit": 5}'
            )
        )
        mock_resource.list.assert_called_once_with({"limit": 5})

    def test_list_missing_creds(self):
        with patch.dict(os.environ, {}, clear=True), patch(
            "augur_mcp.server._load_all_creds", return_value=[]
        ):
            result = json.loads(
                augur_list(service="agr-site", resource="settings")
            )
            assert "error" in result

    def test_list_unknown_service(self, mock_api):
        set_api(mock_api)
        result = json.loads(augur_list(service="nonexistent", resource="items"))
        assert "error" in result
        assert "Unknown service" in result["detail"]

    @pytest.mark.usefixtures("_catalog_loaded")
    def test_list_with_site_param(self, mock_api):
        set_api(mock_api, site="my-site")
        mock_response = MagicMock()
        mock_response.model_dump.return_value = {"data": [], "count": 0}
        mock_resource = MagicMock()
        mock_resource.list.return_value = mock_response
        mock_svc = MagicMock()
        mock_svc.settings = mock_resource
        type(mock_api).agr_site = PropertyMock(return_value=mock_svc)

        result = json.loads(
            augur_list(
                service="agr-site", resource="settings", site="my-site"
            )
        )
        assert "error" not in result


# --- augur_get ---


class TestAugurGet:
    @pytest.mark.usefixtures("_catalog_loaded")
    def test_get_success(self, mock_api):
        set_api(mock_api)
        mock_response = MagicMock()
        mock_response.model_dump.return_value = {"data": {"id": 5, "name": "test"}}
        mock_resource = MagicMock()
        mock_resource.get.return_value = mock_response
        mock_svc = MagicMock()
        mock_svc.settings = mock_resource
        type(mock_api).agr_site = PropertyMock(return_value=mock_svc)

        result = json.loads(
            augur_get(service="agr-site", resource="settings", record_id="5")
        )
        assert result["data"]["id"] == 5
        mock_resource.get.assert_called_once_with(5)

    def test_get_error(self, mock_api):
        set_api(mock_api)
        result = json.loads(
            augur_get(service="nonexistent", resource="x", record_id="1")
        )
        assert "error" in result

    @pytest.mark.usefixtures("_catalog_loaded")
    def test_get_with_site_param(self, mock_api):
        set_api(mock_api, site="my-site")
        mock_response = MagicMock()
        mock_response.model_dump.return_value = {"data": {"id": 1}}
        mock_resource = MagicMock()
        mock_resource.get.return_value = mock_response
        mock_svc = MagicMock()
        mock_svc.settings = mock_resource
        type(mock_api).agr_site = PropertyMock(return_value=mock_svc)

        result = json.loads(
            augur_get(
                service="agr-site",
                resource="settings",
                record_id="1",
                site="my-site",
            )
        )
        assert "error" not in result


# --- augur_create ---


class TestAugurCreate:
    @pytest.mark.usefixtures("_catalog_loaded")
    def test_create_success(self, mock_api):
        set_api(mock_api)
        mock_response = MagicMock()
        mock_response.model_dump.return_value = {"data": {"id": 10, "name": "new"}}
        mock_resource = MagicMock()
        mock_resource.create.return_value = mock_response
        mock_svc = MagicMock()
        mock_svc.settings = mock_resource
        type(mock_api).agr_site = PropertyMock(return_value=mock_svc)

        result = json.loads(
            augur_create(
                service="agr-site", resource="settings", data='{"name": "new"}'
            )
        )
        assert result["data"]["name"] == "new"
        mock_resource.create.assert_called_once_with({"name": "new"})

    def test_create_error(self, mock_api):
        set_api(mock_api)
        result = json.loads(
            augur_create(service="nonexistent", resource="x", data='{"a": 1}')
        )
        assert "error" in result

    @pytest.mark.usefixtures("_catalog_loaded")
    def test_create_with_site_param(self, mock_api):
        set_api(mock_api, site="my-site")
        mock_response = MagicMock()
        mock_response.model_dump.return_value = {"data": {"id": 1}}
        mock_resource = MagicMock()
        mock_resource.create.return_value = mock_response
        mock_svc = MagicMock()
        mock_svc.settings = mock_resource
        type(mock_api).agr_site = PropertyMock(return_value=mock_svc)

        result = json.loads(
            augur_create(
                service="agr-site",
                resource="settings",
                data='{"x": 1}',
                site="my-site",
            )
        )
        assert "error" not in result


# --- augur_update ---


class TestAugurUpdate:
    @pytest.mark.usefixtures("_catalog_loaded")
    def test_update_success(self, mock_api):
        set_api(mock_api)
        mock_response = MagicMock()
        mock_response.model_dump.return_value = {
            "data": {"id": 5, "name": "updated"},
        }
        mock_resource = MagicMock()
        mock_resource.update.return_value = mock_response
        mock_svc = MagicMock()
        mock_svc.settings = mock_resource
        type(mock_api).agr_site = PropertyMock(return_value=mock_svc)

        result = json.loads(
            augur_update(
                service="agr-site",
                resource="settings",
                record_id="5",
                data='{"name": "updated"}',
            )
        )
        assert result["data"]["name"] == "updated"
        mock_resource.update.assert_called_once_with(5, {"name": "updated"})

    def test_update_error(self, mock_api):
        set_api(mock_api)
        result = json.loads(
            augur_update(
                service="nonexistent", resource="x", record_id="1", data='{"a": 1}'
            )
        )
        assert "error" in result

    @pytest.mark.usefixtures("_catalog_loaded")
    def test_update_with_site_param(self, mock_api):
        set_api(mock_api, site="my-site")
        mock_response = MagicMock()
        mock_response.model_dump.return_value = {"data": {"id": 5}}
        mock_resource = MagicMock()
        mock_resource.update.return_value = mock_response
        mock_svc = MagicMock()
        mock_svc.settings = mock_resource
        type(mock_api).agr_site = PropertyMock(return_value=mock_svc)

        result = json.loads(
            augur_update(
                service="agr-site",
                resource="settings",
                record_id="5",
                data='{"x": 1}',
                site="my-site",
            )
        )
        assert "error" not in result


# --- augur_delete ---


class TestAugurDelete:
    @pytest.mark.usefixtures("_catalog_loaded")
    def test_delete_success(self, mock_api):
        set_api(mock_api)
        mock_response = MagicMock()
        mock_response.model_dump.return_value = {"data": True}
        mock_resource = MagicMock()
        mock_resource.delete.return_value = mock_response
        mock_svc = MagicMock()
        mock_svc.settings = mock_resource
        type(mock_api).agr_site = PropertyMock(return_value=mock_svc)

        result = json.loads(
            augur_delete(service="agr-site", resource="settings", record_id="5")
        )
        assert result["data"] is True
        mock_resource.delete.assert_called_once_with(5)

    def test_delete_error(self, mock_api):
        set_api(mock_api)
        result = json.loads(
            augur_delete(service="nonexistent", resource="x", record_id="1")
        )
        assert "error" in result

    @pytest.mark.usefixtures("_catalog_loaded")
    def test_delete_with_site_param(self, mock_api):
        set_api(mock_api, site="my-site")
        mock_response = MagicMock()
        mock_response.model_dump.return_value = {"data": True}
        mock_resource = MagicMock()
        mock_resource.delete.return_value = mock_response
        mock_svc = MagicMock()
        mock_svc.settings = mock_resource
        type(mock_api).agr_site = PropertyMock(return_value=mock_svc)

        result = json.loads(
            augur_delete(
                service="agr-site",
                resource="settings",
                record_id="5",
                site="my-site",
            )
        )
        assert "error" not in result


# --- augur_create / augur_update empty data ---


class TestEmptyData:
    @pytest.mark.usefixtures("_catalog_loaded")
    def test_create_empty_data(self, mock_api):
        set_api(mock_api)
        mock_response = MagicMock()
        mock_response.model_dump.return_value = {"data": {}}
        mock_resource = MagicMock()
        mock_resource.create.return_value = mock_response
        mock_svc = MagicMock()
        mock_svc.settings = mock_resource
        type(mock_api).agr_site = PropertyMock(return_value=mock_svc)

        result = json.loads(
            augur_create(service="agr-site", resource="settings", data="")
        )
        assert "data" in result
        mock_resource.create.assert_called_once_with({})

    @pytest.mark.usefixtures("_catalog_loaded")
    def test_update_empty_data(self, mock_api):
        set_api(mock_api)
        mock_response = MagicMock()
        mock_response.model_dump.return_value = {"data": {}}
        mock_resource = MagicMock()
        mock_resource.update.return_value = mock_response
        mock_svc = MagicMock()
        mock_svc.settings = mock_resource
        type(mock_api).agr_site = PropertyMock(return_value=mock_svc)

        result = json.loads(
            augur_update(
                service="agr-site", resource="settings", record_id="1", data=""
            )
        )
        assert "data" in result

    @pytest.mark.usefixtures("_catalog_loaded")
    def test_list_empty_params(self, mock_api):
        set_api(mock_api)
        mock_response = MagicMock()
        mock_response.model_dump.return_value = {"data": [], "count": 0}
        mock_resource = MagicMock()
        mock_resource.list.return_value = mock_response
        mock_svc = MagicMock()
        mock_svc.settings = mock_resource
        type(mock_api).agr_site = PropertyMock(return_value=mock_svc)

        result = json.loads(
            augur_list(service="agr-site", resource="settings", params="")
        )
        assert result["count"] == 0


# --- Multi-site routing ---


class TestMultiSiteRouting:
    @pytest.mark.usefixtures("_catalog_loaded")
    def test_site_routes_to_correct_client(self):
        """Each site param routes to its specific client."""
        mock_a = MagicMock()
        mock_b = MagicMock()
        set_api(mock_a, site="site-a")
        set_api(mock_b, site="site-b")

        mock_response = MagicMock()
        mock_response.model_dump.return_value = {"data": [], "count": 0}
        mock_resource = MagicMock()
        mock_resource.list.return_value = mock_response
        mock_svc = MagicMock()
        mock_svc.settings = mock_resource
        type(mock_a).agr_site = PropertyMock(return_value=mock_svc)

        augur_list(service="agr-site", resource="settings", site="site-a")
        mock_resource.list.assert_called_once()

    def test_missing_site_with_multi_clients(self):
        """Error when site omitted with multiple clients."""
        set_api(MagicMock(), site="a")
        set_api(MagicMock(), site="b")
        result = json.loads(augur_list(service="s", resource="r"))
        assert "error" in result
        assert "Multiple sites" in result["detail"]


# --- main entry point ---


class TestMain:
    def test_main_calls_init_and_run(self):
        from augur_mcp import server

        with patch.object(server, "init_catalog") as mock_init, patch.object(
            server.mcp, "run"
        ) as mock_run:
            server.main()
            mock_init.assert_called_once()
            mock_run.assert_called_once()
