"""Tests for vm_fleet_status MCP tool.

Uses monkeypatch to replace ``default_registry`` and ``default_job_store``
so tests run against isolated in-memory / tmp_path-backed stores.
"""

from __future__ import annotations

from typing import TYPE_CHECKING

import pytest

import mcp_vm_blackbox.fleet_tools as fleet_tools_mod
from mcp_vm_blackbox.coordination_db import _reset_db_instance_for_testing
from mcp_vm_blackbox.fleet_tools import vm_fleet_status
from mcp_vm_blackbox.job_store import JobStore
from mcp_vm_blackbox.vm_registry import VMRegistry

if TYPE_CHECKING:
    from collections.abc import Generator
    from pathlib import Path


@pytest.fixture(autouse=True)
def reset_db_singleton() -> Generator[None, None, None]:
    """Reset the CoordinationDB singleton before and after each test to prevent xdist state leakage."""
    _reset_db_instance_for_testing()
    yield
    _reset_db_instance_for_testing()


class TestVmFleetStatus:
    """Unit tests for the vm_fleet_status tool."""

    @pytest.fixture(autouse=True)
    def _isolated_stores(self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
        """Replace module-level singletons with tmp_path-backed stores."""
        self.registry = VMRegistry(tmp_path)
        self.job_store = JobStore(tmp_path)
        monkeypatch.setattr(fleet_tools_mod, "default_registry", self.registry)
        monkeypatch.setattr(fleet_tools_mod, "default_job_store", self.job_store)

    @pytest.mark.anyio
    async def test_empty_registry_returns_empty_list(self) -> None:
        """Empty registry produces an empty list."""
        result = await vm_fleet_status()
        assert result == []

    @pytest.mark.anyio
    async def test_vms_with_mixed_jobs(self) -> None:
        """Two VMs — one with 2 jobs (active + completed), one with none."""
        from mcp_vm_blackbox.models import VmListEntry

        # Populate registry with 2 VMs
        await self.registry.populate([
            VmListEntry(name="vm-alpha", uuid="uuid-aaa", running=True),
            VmListEntry(name="vm-beta", uuid="uuid-bbb", running=False),
        ])

        # Create 2 jobs on vm-alpha
        job1 = await self.job_store.start("vm-alpha", "install", "agent-1", "begin install")
        job2 = await self.job_store.start("vm-alpha", "smoke", "agent-2", "start smoke test")
        await self.job_store.set_status(job2, "vm-alpha", "completed")

        fleet = await vm_fleet_status()

        assert len(fleet) == 2

        # Find entries by vm_name
        alpha = next(e for e in fleet if e.vm_name == "vm-alpha")
        beta = next(e for e in fleet if e.vm_name == "vm-beta")

        # vm-alpha: 2 jobs
        assert alpha.uuid == "uuid-aaa"
        assert alpha.running is True
        assert len(alpha.jobs) == 2

        # Verify job structure
        job_ids = {j.job_id for j in alpha.jobs}
        assert job1 in job_ids
        assert job2 in job_ids

        active_job = next(j for j in alpha.jobs if j.job_id == job1)
        assert active_job.status == "active"
        assert active_job.scenario_name == "install"
        assert active_job.last_agent_id == "agent-1"
        assert active_job.last_step == "begin install"
        assert active_job.updated_at is not None

        completed_job = next(j for j in alpha.jobs if j.job_id == job2)
        assert completed_job.status == "completed"

        # vm-beta: 0 jobs
        assert beta.uuid == "uuid-bbb"
        assert beta.running is False
        assert beta.jobs == []

    @pytest.mark.anyio
    async def test_vm_with_no_job_directory(self) -> None:
        """VM exists in registry but no jobs directory — returns empty jobs list."""
        from mcp_vm_blackbox.models import VmListEntry

        # Populate registry with 1 VM — no jobs created at all
        await self.registry.populate([VmListEntry(name="vm-lonely", uuid="uuid-ccc", running=True)])

        fleet = await vm_fleet_status()

        assert len(fleet) == 1
        entry = fleet[0]
        assert entry.vm_name == "vm-lonely"
        assert entry.uuid == "uuid-ccc"
        assert entry.running is True
        assert entry.jobs == []
