"""Tests for guest_file_tools MCP tools.

Tests: vm_copy_from_guest.
How: Mock _vbox, _resolve so tests run offline.
Why: CI environments lack VirtualBox; tools must return correct models and errors.
"""

from __future__ import annotations

from typing import TYPE_CHECKING
from unittest.mock import AsyncMock, MagicMock

import pytest
from fastmcp.exceptions import ToolError

from mcp_vm_blackbox.targets import TargetConfig

if TYPE_CHECKING:
    from pytest_mock import MockerFixture


# ---------------------------------------------------------------------------
# Fixtures
# ---------------------------------------------------------------------------


@pytest.fixture
def mock_resolve(mocker: MockerFixture) -> MagicMock:
    """Patch _resolve to return a local TargetConfig (ssh_host='')."""
    cfg = TargetConfig(ssh_host="", winrm_port=5985)
    return mocker.patch("mcp_vm_blackbox.guest_file_tools._resolve", return_value=cfg)


@pytest.fixture
def mock_vbox(mocker: MockerFixture) -> MagicMock:
    """Patch _vbox to return a mock VBoxDispatch with async methods."""
    mock_dispatch = MagicMock()
    mock_dispatch.copy_from_vm = AsyncMock(return_value=None)
    return mocker.patch("mcp_vm_blackbox.guest_file_tools._vbox", return_value=mock_dispatch)


# ---------------------------------------------------------------------------
# vm_copy_from_guest
# ---------------------------------------------------------------------------


class TestVmCopyFromGuest:
    """Tests for vm_copy_from_guest tool."""

    @pytest.mark.asyncio
    async def test_success_returns_correct_result(self, mock_resolve: MagicMock, mock_vbox: MagicMock) -> None:
        """vm_copy_from_guest returns VmCopyResult with success=True on success."""
        from mcp_vm_blackbox.guest_file_tools import vm_copy_from_guest

        result = await vm_copy_from_guest(
            vm_name="test-vm",
            guest_path="C:\\Users\\User\\Desktop\\proof.txt",
            host_path="/tmp/proof.txt",
            target="local",
        )

        assert result.success is True
        assert result.guest_path == "C:\\Users\\User\\Desktop\\proof.txt"
        assert result.host_path == "/tmp/proof.txt"
        assert result.error is None

        # Verify copy_from_vm was called with correct args
        mock_vbox.return_value.copy_from_vm.assert_awaited_once_with(
            guest_src="C:\\Users\\User\\Desktop\\proof.txt",
            local_dst="/tmp/proof.txt",
            username="vagrant",
            password="vagrant",
        )

    @pytest.mark.asyncio
    async def test_custom_credentials(self, mock_resolve: MagicMock, mock_vbox: MagicMock) -> None:
        """vm_copy_from_guest passes custom credentials to VBoxDispatch."""
        from mcp_vm_blackbox.guest_file_tools import vm_copy_from_guest

        result = await vm_copy_from_guest(
            vm_name="test-vm",
            guest_path="/home/user/file.txt",
            host_path="/tmp/file.txt",
            target="local",
            username="admin",
            password="secret",
        )

        assert result.success is True
        mock_vbox.return_value.copy_from_vm.assert_awaited_once_with(
            guest_src="/home/user/file.txt", local_dst="/tmp/file.txt", username="admin", password="secret"
        )

    @pytest.mark.asyncio
    async def test_runtime_error_maps_to_tool_error(self, mock_resolve: MagicMock, mock_vbox: MagicMock) -> None:
        """vm_copy_from_guest raises ToolError when VBoxManage fails."""
        mock_vbox.return_value.copy_from_vm.side_effect = RuntimeError(
            "VBoxManage copyfrom failed: No such file or directory"
        )

        from mcp_vm_blackbox.guest_file_tools import vm_copy_from_guest

        with pytest.raises(ToolError, match="Failed to copy"):
            await vm_copy_from_guest(
                vm_name="test-vm", guest_path="C:\\nonexistent.txt", host_path="/tmp/nonexistent.txt", target="local"
            )

    @pytest.mark.asyncio
    async def test_vm_not_found_error(self, mock_resolve: MagicMock, mock_vbox: MagicMock) -> None:
        """vm_copy_from_guest raises ToolError when VM is not found."""
        mock_vbox.return_value.copy_from_vm.side_effect = RuntimeError(
            "VBoxManage copyfrom failed: Could not find a registered machine"
        )

        from mcp_vm_blackbox.guest_file_tools import vm_copy_from_guest

        with pytest.raises(ToolError, match=r"Failed to copy.*Could not find"):
            await vm_copy_from_guest(
                vm_name="missing-vm", guest_path="C:\\file.txt", host_path="/tmp/file.txt", target="local"
            )

    @pytest.mark.asyncio
    async def test_authentication_error(self, mock_resolve: MagicMock, mock_vbox: MagicMock) -> None:
        """vm_copy_from_guest raises ToolError on authentication failure."""
        mock_vbox.return_value.copy_from_vm.side_effect = RuntimeError(
            "VBoxManage copyfrom failed: Authentication failed"
        )

        from mcp_vm_blackbox.guest_file_tools import vm_copy_from_guest

        with pytest.raises(ToolError, match="Authentication failed"):
            await vm_copy_from_guest(
                vm_name="test-vm",
                guest_path="C:\\file.txt",
                host_path="/tmp/file.txt",
                target="local",
                username="wronguser",
                password="wrongpass",
            )

    @pytest.mark.asyncio
    async def test_remote_target(self, mocker: MockerFixture, mock_vbox: MagicMock) -> None:
        """vm_copy_from_guest works with remote targets."""
        # Create a remote target config
        cfg = TargetConfig(ssh_host="user@remote-host", ssh_port=22, winrm_port=5985)
        mocker.patch("mcp_vm_blackbox.guest_file_tools._resolve", return_value=cfg)

        from mcp_vm_blackbox.guest_file_tools import vm_copy_from_guest

        result = await vm_copy_from_guest(
            vm_name="remote-vm", guest_path="/home/user/file.txt", host_path="/tmp/remote-file.txt", target="ci"
        )

        assert result.success is True
        # Verify _resolve was called with the target
        mock_vbox.return_value.copy_from_vm.assert_awaited_once()
