"""Tests for CloneBox adapter — mock subprocess, verify argument mapping."""

from __future__ import annotations

import subprocess
from pathlib import Path
from unittest.mock import MagicMock, patch

import pytest

from proxym.virt.clonebox_adapter import CloneBoxAdapter, CloneBoxResult


@pytest.fixture
def adapter(tmp_path):
    """Adapter with a temporary profiles directory."""
    profiles = tmp_path / "profiles"
    profiles.mkdir()
    a = CloneBoxAdapter(profiles_dir=str(profiles))
    # Force-set binary to avoid PATH lookup
    a._clonebox_bin = "/usr/bin/clonebox"
    return a


@pytest.fixture
def mock_run():
    """Patch subprocess.run to capture clonebox CLI calls."""
    with patch("proxym.virt.clonebox_adapter.subprocess.run") as m:
        m.return_value = subprocess.CompletedProcess(
            args=[], returncode=0, stdout="ok", stderr=""
        )
        yield m


# ── Availability ─────────────────────────────────────────────

class TestAvailability:
    def test_is_available_when_binary_found(self, adapter):
        with patch("proxym.virt.clonebox_adapter.shutil.which", return_value="/usr/bin/clonebox"):
            assert adapter.is_available()

    def test_not_available_when_binary_missing(self):
        a = CloneBoxAdapter()
        with patch("proxym.virt.clonebox_adapter.shutil.which", return_value=None):
            assert not a.is_available()

    def test_version_returns_string(self, adapter, mock_run):
        mock_run.return_value.stdout = "clonebox 1.2.0"
        assert adapter.version() == "clonebox 1.2.0"

    def test_version_returns_none_on_failure(self, adapter, mock_run):
        mock_run.return_value.returncode = 1
        assert adapter.version() is None


# ── Create VM ────────────────────────────────────────────────

class TestCreateVM:
    def test_create_vm_basic(self, adapter, mock_run):
        result = adapter.create_vm("windsurf-work")
        assert result.success
        args = mock_run.call_args[0][0]
        assert args == ["/usr/bin/clonebox", "clone", ".", "--name", "windsurf-work",
                        "--user", "--no-run"]

    def test_create_vm_with_profile(self, adapter, mock_run, tmp_path):
        profile = tmp_path / "windsurf.yaml"
        profile.write_text("name: test")
        result = adapter.create_vm("test-vm", profile_path=profile)
        assert result.success
        args = mock_run.call_args[0][0]
        assert "--profile" in args
        assert str(profile) in args

    def test_create_vm_with_extra_args(self, adapter, mock_run):
        result = adapter.create_vm("test-vm", extra_args={"memory": "8192"})
        assert result.success
        args = mock_run.call_args[0][0]
        assert "--memory" in args
        assert "8192" in args

    def test_create_vm_failure(self, adapter, mock_run):
        mock_run.return_value = subprocess.CompletedProcess(
            args=[], returncode=1, stdout="", stderr="disk full"
        )
        result = adapter.create_vm("fail-vm")
        assert not result.success
        assert "disk full" in result.stderr


# ── Start / Stop / Snapshot ──────────────────────────────────

class TestLifecycle:
    def test_start_vm(self, adapter, mock_run):
        result = adapter.start_vm("windsurf-work")
        assert result.success
        args = mock_run.call_args[0][0]
        assert args == ["/usr/bin/clonebox", "start", "windsurf-work", "--user"]

    def test_stop_vm(self, adapter, mock_run):
        result = adapter.stop_vm("windsurf-work")
        assert result.success
        args = mock_run.call_args[0][0]
        assert args == ["/usr/bin/clonebox", "stop", "windsurf-work", "--user"]

    def test_snapshot(self, adapter, mock_run):
        result = adapter.snapshot("windsurf-work", "before-refactor")
        assert result.success
        args = mock_run.call_args[0][0]
        assert args == ["/usr/bin/clonebox", "snapshot", "create", "windsurf-work",
                        "--name", "before-refactor", "--user"]

    def test_restore_snapshot(self, adapter, mock_run):
        result = adapter.restore_snapshot("windsurf-work", "before-refactor")
        assert result.success
        args = mock_run.call_args[0][0]
        assert "restore" in args

    def test_health(self, adapter, mock_run):
        mock_run.return_value.stdout = "running"
        result = adapter.health("windsurf-work")
        assert result.success
        assert "running" in result.stdout

    def test_delete_vm(self, adapter, mock_run):
        result = adapter.delete_vm("windsurf-work")
        assert result.success
        args = mock_run.call_args[0][0]
        assert "delete" in args
        assert "--force" in args

    def test_list_vms(self, adapter, mock_run):
        mock_run.return_value.stdout = '[]'
        result = adapter.list_vms()
        assert result.success
        assert "--json" in mock_run.call_args[0][0]


# ── Error Handling ───────────────────────────────────────────

class TestErrorHandling:
    def test_timeout_returns_failure(self, adapter):
        with patch("proxym.virt.clonebox_adapter.subprocess.run",
                   side_effect=subprocess.TimeoutExpired(cmd="test", timeout=60)):
            result = adapter.start_vm("test")
            assert not result.success
            assert "timeout" in result.stderr

    def test_generic_exception_returns_failure(self, adapter):
        with patch("proxym.virt.clonebox_adapter.subprocess.run",
                   side_effect=OSError("permission denied")):
            result = adapter.start_vm("test")
            assert not result.success
            assert "permission denied" in result.stderr

    def test_binary_not_found(self):
        a = CloneBoxAdapter()
        a._clonebox_bin = None
        with patch("proxym.virt.clonebox_adapter.shutil.which", return_value=None):
            result = a.start_vm("test")
            assert not result.success
            assert "not found" in result.stderr


# ── Profile Management ───────────────────────────────────────

class TestProfiles:
    def test_get_profile_path_exists(self, adapter, tmp_path):
        profile = Path(adapter.profiles_dir) / "windsurf.clonebox.yaml"
        profile.write_text("name: test")
        assert adapter.get_profile_path("windsurf") == profile

    def test_get_profile_path_missing(self, adapter):
        assert adapter.get_profile_path("nonexistent") is None

    def test_generate_profile_name(self, adapter):
        name = adapter.generate_profile_name("windsurf", "Work Anthropic")
        assert name == "windsurf-work-anthropic"

    def test_generate_profile_name_underscores(self, adapter):
        name = adapter.generate_profile_name("cursor", "my_account")
        assert name == "cursor-my-account"
