"""Tests for core/mcp/config.py — MCP config persistence."""

from __future__ import annotations

import json

import pytest

from icarus.mcp.config import (
    ServerEntry,
    add_server,
    list_servers,
    read_mcp_config,
    remove_server,
    toggle_server,
    update_server,
    write_mcp_config,
)


# ---------------------------------------------------------------------------
# ServerEntry
# ---------------------------------------------------------------------------


class TestServerEntry:
    def test_stdio_transport(self):
        e = ServerEntry("test", True, {"command": "node", "args": ["server.js"]})
        assert e.transport_type == "stdio"

    def test_http_transport(self):
        e = ServerEntry("test", True, {"url": "https://example.com/mcp"})
        assert e.transport_type == "http"

    def test_http_with_type_field(self):
        e = ServerEntry("test", True, {"type": "http", "url": "https://example.com"})
        assert e.transport_type == "http"


# ---------------------------------------------------------------------------
# read_mcp_config
# ---------------------------------------------------------------------------


class TestReadConfig:
    def test_missing_file(self, tmp_path):
        config = read_mcp_config(tmp_path / "nonexistent.json")
        assert config == {"mcpServers": {}, "_disabled": {}}

    def test_valid_config(self, tmp_path):
        path = tmp_path / "mcp.json"
        data = {
            "mcpServers": {"s1": {"command": "echo"}},
            "_disabled": {"s2": {"command": "cat"}},
        }
        path.write_text(json.dumps(data))
        config = read_mcp_config(path)
        assert "s1" in config["mcpServers"]
        assert "s2" in config["_disabled"]

    def test_invalid_json(self, tmp_path):
        path = tmp_path / "mcp.json"
        path.write_text("not json")
        config = read_mcp_config(path)
        assert config == {"mcpServers": {}, "_disabled": {}}

    def test_missing_keys_added(self, tmp_path):
        path = tmp_path / "mcp.json"
        path.write_text(json.dumps({"mcpServers": {"s1": {"command": "echo"}}}))
        config = read_mcp_config(path)
        assert "_disabled" in config
        assert config["_disabled"] == {}


# ---------------------------------------------------------------------------
# write_mcp_config
# ---------------------------------------------------------------------------


class TestWriteConfig:
    def test_write_creates_file(self, tmp_path):
        path = tmp_path / "mcp.json"
        config = {"mcpServers": {"s1": {"command": "echo"}}, "_disabled": {}}
        write_mcp_config(path, config)
        assert path.exists()
        loaded = json.loads(path.read_text())
        assert loaded["mcpServers"]["s1"]["command"] == "echo"

    def test_write_creates_parent_dirs(self, tmp_path):
        path = tmp_path / "nested" / "dir" / "mcp.json"
        write_mcp_config(path, {"mcpServers": {}, "_disabled": {}})
        assert path.exists()

    def test_atomic_write(self, tmp_path):
        """No .tmp file left behind after write."""
        path = tmp_path / "mcp.json"
        write_mcp_config(path, {"mcpServers": {}, "_disabled": {}})
        assert not path.with_suffix(".tmp").exists()


# ---------------------------------------------------------------------------
# list_servers
# ---------------------------------------------------------------------------


class TestListServers:
    def test_empty(self):
        entries = list_servers({"mcpServers": {}, "_disabled": {}})
        assert entries == []

    def test_mixed(self):
        config = {
            "mcpServers": {"a": {"command": "echo"}, "b": {"url": "http://x"}},
            "_disabled": {"c": {"command": "cat"}},
        }
        entries = list_servers(config)
        assert len(entries) == 3
        names = {e.name for e in entries}
        assert names == {"a", "b", "c"}
        enabled = {e.name for e in entries if e.enabled}
        assert enabled == {"a", "b"}

    def test_transport_types(self):
        config = {
            "mcpServers": {"stdio": {"command": "echo"}, "http": {"url": "http://x"}},
            "_disabled": {},
        }
        entries = {e.name: e for e in list_servers(config)}
        assert entries["stdio"].transport_type == "stdio"
        assert entries["http"].transport_type == "http"


# ---------------------------------------------------------------------------
# add_server
# ---------------------------------------------------------------------------


class TestAddServer:
    def test_add_new(self):
        config = {"mcpServers": {}, "_disabled": {}}
        add_server(config, "s1", {"command": "echo"})
        assert "s1" in config["mcpServers"]

    def test_add_duplicate_enabled(self):
        config = {"mcpServers": {"s1": {"command": "echo"}}, "_disabled": {}}
        with pytest.raises(ValueError, match="already exists"):
            add_server(config, "s1", {"command": "cat"})

    def test_add_duplicate_disabled(self):
        config = {"mcpServers": {}, "_disabled": {"s1": {"command": "echo"}}}
        with pytest.raises(ValueError, match="already exists"):
            add_server(config, "s1", {"command": "cat"})


# ---------------------------------------------------------------------------
# update_server
# ---------------------------------------------------------------------------


class TestUpdateServer:
    def test_update_enabled(self):
        config = {"mcpServers": {"s1": {"command": "echo"}}, "_disabled": {}}
        update_server(config, "s1", {"command": "cat"})
        assert config["mcpServers"]["s1"]["command"] == "cat"

    def test_update_disabled(self):
        config = {"mcpServers": {}, "_disabled": {"s1": {"command": "echo"}}}
        update_server(config, "s1", {"command": "cat"})
        assert config["_disabled"]["s1"]["command"] == "cat"

    def test_update_not_found(self):
        config = {"mcpServers": {}, "_disabled": {}}
        with pytest.raises(ValueError, match="not found"):
            update_server(config, "s1", {"command": "echo"})


# ---------------------------------------------------------------------------
# remove_server
# ---------------------------------------------------------------------------


class TestRemoveServer:
    def test_remove_enabled(self):
        config = {"mcpServers": {"s1": {"command": "echo"}}, "_disabled": {}}
        remove_server(config, "s1")
        assert "s1" not in config["mcpServers"]

    def test_remove_disabled(self):
        config = {"mcpServers": {}, "_disabled": {"s1": {"command": "echo"}}}
        remove_server(config, "s1")
        assert "s1" not in config["_disabled"]

    def test_remove_not_found(self):
        config = {"mcpServers": {}, "_disabled": {}}
        with pytest.raises(ValueError, match="not found"):
            remove_server(config, "s1")


# ---------------------------------------------------------------------------
# toggle_server
# ---------------------------------------------------------------------------


class TestToggleServer:
    def test_disable(self):
        config = {"mcpServers": {"s1": {"command": "echo"}}, "_disabled": {}}
        toggle_server(config, "s1")
        assert "s1" not in config["mcpServers"]
        assert "s1" in config["_disabled"]
        assert config["_disabled"]["s1"]["command"] == "echo"

    def test_enable(self):
        config = {"mcpServers": {}, "_disabled": {"s1": {"command": "echo"}}}
        toggle_server(config, "s1")
        assert "s1" in config["mcpServers"]
        assert "s1" not in config["_disabled"]

    def test_toggle_not_found(self):
        config = {"mcpServers": {}, "_disabled": {}}
        with pytest.raises(ValueError, match="not found"):
            toggle_server(config, "s1")

    def test_roundtrip(self):
        config = {"mcpServers": {"s1": {"command": "echo"}}, "_disabled": {}}
        toggle_server(config, "s1")  # disable
        toggle_server(config, "s1")  # re-enable
        assert "s1" in config["mcpServers"]
        assert config["mcpServers"]["s1"]["command"] == "echo"
