"""Unit tests for cluster_define tool."""

from __future__ import annotations

from unittest.mock import AsyncMock, MagicMock, patch

import pytest


@pytest.fixture
def mock_topology():
    """A mock TopologySpec for testing."""
    from vmcluster_mcp.topology.schema import NetworkSpec, NodeSpec, SshSpec, TopologySpec

    return TopologySpec(
        cluster_name="testcluster",
        base_image="/images/ubuntu.qcow2",
        overlay_dir="/overlays",
        network=NetworkSpec(name="testcluster-net", bridge="virbr100", subnet="192.168.100.0/24"),
        nodes=[
            NodeSpec(name="node1", role="worker", vcpus=2, memory_mb=2048, ip="192.168.100.10"),
            NodeSpec(name="node2", role="worker", vcpus=2, memory_mb=2048, ip="192.168.100.11"),
        ],
        ssh=SshSpec(key_path="/etc/vmcluster/ssh/id_ed25519"),
    )


@pytest.mark.asyncio
async def test_cluster_define_creates_network_and_domains(mock_topology):
    """cluster_define creates network + domains for all nodes."""
    with (
        patch("vmcluster_mcp.cluster.load_topology", return_value=mock_topology),
        patch("vmcluster_mcp.cluster._client") as mock_client,
        patch("vmcluster_mcp.cluster.subprocess.run"),
        patch("vmcluster_mcp.cluster.generate_cloud_init_iso", return_value="/overlays/testcluster/node1-cidata.iso"),
        patch("os.makedirs"),
    ):
        mock_client.lookup_domain = AsyncMock(return_value=None)
        mock_client.lookup_network = AsyncMock(return_value=None)
        mock_net = MagicMock()
        mock_client.define_network = AsyncMock(return_value=mock_net)
        mock_client.start_network = AsyncMock()
        mock_client.define_domain = AsyncMock(return_value=MagicMock())
        mock_client.is_network_active = AsyncMock(return_value=True)
        mock_client.is_domain_active = AsyncMock(return_value=False)
        mock_client.get_domain_state = AsyncMock(return_value="shut off")

        from vmcluster_mcp.cluster import cluster_define

        result = await cluster_define(topology_name="testcluster")

    assert result.success is True
    assert result.result is not None
    assert result.result.cluster_name == "testcluster"
    assert len(result.result.nodes) == 2
    # Network should have been defined and started
    mock_client.define_network.assert_called_once()
    mock_client.start_network.assert_called_once()
    # Domains should have been defined for each node
    assert mock_client.define_domain.call_count == 2


@pytest.mark.asyncio
async def test_cluster_define_idempotent(mock_topology):
    """cluster_define returns success without re-creating if cluster already exists."""
    with (
        patch("vmcluster_mcp.cluster.load_topology", return_value=mock_topology),
        patch("vmcluster_mcp.cluster._client") as mock_client,
    ):
        # First domain already exists → idempotent path
        mock_client.lookup_domain = AsyncMock(return_value=MagicMock())
        mock_client.get_domain_state = AsyncMock(return_value="shut off")
        mock_client.is_domain_active = AsyncMock(return_value=False)
        mock_net = MagicMock()
        mock_client.lookup_network = AsyncMock(return_value=mock_net)
        mock_client.is_network_active = AsyncMock(return_value=True)
        mock_client.define_network = AsyncMock()
        mock_client.define_domain = AsyncMock()

        from vmcluster_mcp.cluster import cluster_define

        result = await cluster_define(topology_name="testcluster")

    assert result.success is True
    # Should NOT have called define_network or define_domain
    mock_client.define_network.assert_not_called()
    mock_client.define_domain.assert_not_called()


@pytest.mark.asyncio
async def test_cluster_define_creates_overlay_dir(mock_topology):
    """cluster_define creates the overlay directory if it doesn't exist."""
    with (
        patch("vmcluster_mcp.cluster.load_topology", return_value=mock_topology),
        patch("vmcluster_mcp.cluster._client") as mock_client,
        patch("vmcluster_mcp.cluster.subprocess.run"),
        patch("vmcluster_mcp.cluster.generate_cloud_init_iso", return_value="/overlays/testcluster/node1-cidata.iso"),
        patch("os.makedirs") as mock_makedirs,
    ):
        mock_client.lookup_domain = AsyncMock(return_value=None)
        mock_client.lookup_network = AsyncMock(return_value=None)
        mock_net = MagicMock()
        mock_client.define_network = AsyncMock(return_value=mock_net)
        mock_client.start_network = AsyncMock()
        mock_client.define_domain = AsyncMock(return_value=MagicMock())
        mock_client.is_network_active = AsyncMock(return_value=True)
        mock_client.is_domain_active = AsyncMock(return_value=False)
        mock_client.get_domain_state = AsyncMock(return_value="shut off")

        from vmcluster_mcp.cluster import cluster_define

        await cluster_define(topology_name="testcluster")

    # makedirs should have been called with exist_ok=True for the cluster overlay dir
    makedirs_calls = [str(c) for c in mock_makedirs.call_args_list]
    assert any("testcluster" in call for call in makedirs_calls), f"testcluster not in makedirs calls: {makedirs_calls}"


@pytest.mark.asyncio
async def test_cluster_define_calls_qemu_img(mock_topology):
    """cluster_define calls qemu-img to create overlay disks."""
    with (
        patch("vmcluster_mcp.cluster.load_topology", return_value=mock_topology),
        patch("vmcluster_mcp.cluster._client") as mock_client,
        patch("vmcluster_mcp.cluster.subprocess.run") as mock_subproc,
        patch("vmcluster_mcp.cluster.generate_cloud_init_iso", return_value="/overlays/testcluster/node1-cidata.iso"),
        patch("os.makedirs"),
    ):
        mock_client.lookup_domain = AsyncMock(return_value=None)
        mock_client.lookup_network = AsyncMock(return_value=None)
        mock_net = MagicMock()
        mock_client.define_network = AsyncMock(return_value=mock_net)
        mock_client.start_network = AsyncMock()
        mock_client.define_domain = AsyncMock(return_value=MagicMock())
        mock_client.is_network_active = AsyncMock(return_value=True)
        mock_client.is_domain_active = AsyncMock(return_value=False)
        mock_client.get_domain_state = AsyncMock(return_value="shut off")

        from vmcluster_mcp.cluster import cluster_define

        await cluster_define(topology_name="testcluster")

    # qemu-img should have been called once per node
    assert mock_subproc.call_count == 2
    for call in mock_subproc.call_args_list:
        cmd = call[0][0]
        assert cmd[0] == "qemu-img"
        assert "create" in cmd
