"""
VersionClient 单元测试
PM-Agent v2.0 - F-032

测试用例: 35个
"""
import pytest
import subprocess
import sys
from pathlib import Path
from unittest.mock import Mock, patch, MagicMock

sys.path.insert(0, str(Path(__file__).parent.parent / "backend"))


class TestVersionClientInit:
    """测试VersionClient初始化"""

    def test_init_with_default_timeout(self):
        """TC-001: 测试默认超时初始化"""
        from backend.services.version_service import VersionClient
        client = VersionClient()
        assert client.timeout == 30

    def test_init_with_custom_timeout(self):
        """TC-002: 测试自定义超时"""
        from backend.services.version_service import VersionClient
        client = VersionClient(timeout=60)
        assert client.timeout == 60


class TestVersionClientCLI:
    """测试CLI可用性检测"""

    @patch('shutil.which')
    def test_cli_available(self, mock_which):
        """TC-003: 测试CLI可用"""
        mock_which.return_value = "/usr/bin/conf-man"
        from backend.services.version_service import VersionClient
        client = VersionClient()
        assert client._is_cli_available() is True

    @patch('shutil.which')
    def test_cli_not_available(self, mock_which):
        """TC-004: 测试CLI不可用"""
        mock_which.return_value = None
        from backend.services.version_service import VersionClient
        client = VersionClient()
        assert client._is_cli_available() is False


class TestVersionClientListVersions:
    """测试列出版本"""

    @patch('subprocess.run')
    @patch('shutil.which')
    def test_list_versions_empty(self, mock_which, mock_run):
        """TC-005: 测试版本列表为空"""
        mock_which.return_value = "/usr/bin/conf-man"
        mock_run.return_value = Mock(returncode=0, stdout='[]')
        
        from backend.services.version_service import VersionClient
        client = VersionClient()
        result = client.list_versions()
        assert result == []

    @patch('subprocess.run')
    @patch('shutil.which')
    def test_list_versions_with_data(self, mock_which, mock_run):
        """TC-006: 测试版本列表有数据"""
        mock_which.return_value = "/usr/bin/conf-man"
        mock_run.return_value = Mock(
            returncode=0,
            stdout='[{"version": "v1.0.0", "status": "released"}]'
        )
        
        from backend.services.version_service import VersionClient
        client = VersionClient()
        result = client.list_versions()
        assert len(result) == 1
        assert result[0]["version"] == "v1.0.0"

    @patch('subprocess.run')
    @patch('shutil.which')
    def test_list_versions_filter_by_status(self, mock_which, mock_run):
        """TC-007: 测试按状态过滤"""
        mock_which.return_value = "/usr/bin/conf-man"
        mock_run.return_value = Mock(
            returncode=0,
            stdout='{"versions": [{"version": "v1.0.0", "status": "draft"}]}'
        )
        
        from backend.services.version_service import VersionClient
        client = VersionClient()
        result = client.list_versions(status="draft")
        assert "versions" in str(mock_run.call_args)


class TestVersionClientShowVersion:
    """测试显示版本详情"""

    @patch('subprocess.run')
    @patch('shutil.which')
    def test_show_version_success(self, mock_which, mock_run):
        """TC-008: 测试显示版本详情成功"""
        mock_which.return_value = "/usr/bin/conf-man"
        mock_run.return_value = Mock(
            returncode=0,
            stdout='{"version": "v1.0.0", "description": "Initial release"}'
        )
        
        from backend.services.version_service import VersionClient
        client = VersionClient()
        result = client.show_version("v1.0.0")
        assert result is not None


class TestVersionClientRegister:
    """测试版本登记"""

    @patch('subprocess.run')
    @patch('shutil.which')
    def test_register_version_success(self, mock_which, mock_run):
        """TC-009: 测试登记新版本成功"""
        mock_which.return_value = "/usr/bin/conf-man"
        mock_run.return_value = Mock(returncode=0, stdout='{"status": "registered"}')
        
        from backend.services.version_service import VersionClient
        client = VersionClient()
        result = client.register_version("v1.0.0")
        assert result is not None


class TestVersionClientRelease:
    """测试版本发布"""

    @patch('subprocess.run')
    @patch('shutil.which')
    def test_release_version_success(self, mock_which, mock_run):
        """TC-010: 测试发布版本成功"""
        mock_which.return_value = "/usr/bin/conf-man"
        mock_run.return_value = Mock(returncode=0, stdout='{"status": "released"}')
        
        from backend.services.version_service import VersionClient
        client = VersionClient()
        result = client.release_version("v1.0.0")
        assert result is not None


class TestVersionClientRollback:
    """测试版本回滚"""

    @patch('subprocess.run')
    @patch('shutil.which')
    def test_rollback_version_success(self, mock_which, mock_run):
        """TC-011: 测试回滚版本成功"""
        mock_which.return_value = "/usr/bin/conf-man"
        mock_run.return_value = Mock(returncode=0, stdout='{"status": "rolled back"}')
        
        from backend.services.version_service import VersionClient
        client = VersionClient()
        result = client.rollback_version("v1.0.0")
        assert result is not None


class TestVersionClientDependencies:
    """测试依赖查询"""

    @patch('subprocess.run')
    @patch('shutil.which')
    def test_get_dependencies_success(self, mock_which, mock_run):
        """TC-012: 测试获取依赖成功"""
        mock_which.return_value = "/usr/bin/conf-man"
        mock_run.return_value = Mock(
            returncode=0,
            stdout='{"manifest": {"dependencies": ["dep1", "dep2"]}}'
        )
        
        from backend.services.version_service import VersionClient
        client = VersionClient()
        result = client.get_dependencies("v1.0.0")
        assert isinstance(result, list)


class TestVersionClientProjects:
    """测试项目管理"""

    @patch('subprocess.run')
    @patch('shutil.which')
    def test_list_projects_success(self, mock_which, mock_run):
        """TC-013: 测试列出项目成功"""
        mock_which.return_value = "/usr/bin/conf-man"
        mock_run.return_value = Mock(
            returncode=0,
            stdout='{"projects": [{"name": "pm-agent"}]}'
        )
        
        from backend.services.version_service import VersionClient
        client = VersionClient()
        result = client.list_projects()
        assert isinstance(result, list)


class TestVersionClientErrors:
    """测试错误处理"""

    @patch('shutil.which')
    def test_cli_execution_error(self, mock_which):
        """TC-014: 测试CLI执行错误"""
        mock_which.return_value = "/usr/bin/conf-man"
        
        from backend.services.version_service import VersionClient
        client = VersionClient()
        
        with patch('subprocess.run', side_effect=RuntimeError("Command failed")):
            with pytest.raises(RuntimeError):
                client.list_versions()

    @patch('subprocess.run')
    @patch('shutil.which')
    def test_invalid_json_response(self, mock_which, mock_run):
        """TC-015: 测试无效JSON响应"""
        mock_which.return_value = "/usr/bin/conf-man"
        mock_run.return_value = Mock(returncode=0, stdout='not valid json')
        
        from backend.services.version_service import VersionClient
        client = VersionClient()
        result = client.list_versions()
        assert isinstance(result, dict)
        assert "message" in result

    @patch('subprocess.run')
    @patch('shutil.which')
    def test_timeout_handling(self, mock_which, mock_run):
        """TC-016: 测试超时处理"""
        mock_which.return_value = "/usr/bin/conf-man"
        mock_run.side_effect = TimeoutError("timeout")
        
        from backend.services.version_service import VersionClient
        client = VersionClient()
        
        with pytest.raises(TimeoutError):
            client.list_versions()


class TestVersionClientEdgeCases:
    """测试边界情况"""

    @patch('subprocess.run')
    @patch('shutil.which')
    def test_empty_version_string(self, mock_which, mock_run):
        """TC-017: 测试空版本字符串"""
        mock_which.return_value = "/usr/bin/conf-man"
        
        from backend.services.version_service import VersionClient
        client = VersionClient()
        
        with patch('subprocess.run', side_effect=RuntimeError()):
            with pytest.raises(RuntimeError):
                client.show_version("")

    @patch('subprocess.run')
    @patch('shutil.which')
    def test_special_chars_in_version(self, mock_which, mock_run):
        """TC-018: 测试版本号特殊字符"""
        mock_which.return_value = "/usr/bin/conf-man"
        mock_run.return_value = Mock(returncode=0, stdout='{}')
        
        from backend.services.version_service import VersionClient
        client = VersionClient()
        result = client.show_version("v1.0.0-alpha+001")
        assert result is not None


class TestVersionClientCurrentVersion:
    """测试当前版本"""

    @patch('subprocess.run')
    @patch('shutil.which')
    def test_get_current_version_success(self, mock_which, mock_run):
        """TC-019: 测试获取当前版本成功"""
        mock_which.return_value = "/usr/bin/conf-man"
        mock_run.return_value = Mock(
            returncode=0,
            stdout='{"version": "v1.0.0"}'
        )
        
        from backend.services.version_service import VersionClient
        client = VersionClient()
        result = client.get_current_version()
        assert result == "v1.0.0"

    @patch('subprocess.run')
    @patch('shutil.which')
    def test_get_current_version_none(self, mock_which, mock_run):
        """TC-020: 测试无当前版本"""
        mock_which.return_value = "/usr/bin/conf-man"
        mock_run.side_effect = Exception("No version")
        
        from backend.services.version_service import VersionClient
        client = VersionClient()
        result = client.get_current_version()
        assert result is None


class TestVersionClientRegisterV2:
    """测试版本登记V2 - TC-021~TC-023"""

    @patch('subprocess.run')
    @patch('shutil.which')
    def test_register_version_with_manifest(self, mock_which, mock_run):
        """TC-021: 测试带清单登记版本"""
        mock_which.return_value = "/usr/bin/conf-man"
        mock_run.return_value = Mock(
            returncode=0,
            stdout='{"status": "registered", "version": "v2.0.0"}'
        )
        
        from backend.services.version_service import VersionClient
        client = VersionClient()
        result = client.register_version("v2.0.0", manifest_path="/path/to/manifest.yaml")
        
        args = mock_run.call_args[0][0]
        assert "--manifest" in args

    @patch('subprocess.run')
    @patch('shutil.which')
    def test_register_version_with_test_report(self, mock_which, mock_run):
        """TC-022: 测试带测试报告登记版本"""
        mock_which.return_value = "/usr/bin/conf-man"
        mock_run.return_value = Mock(
            returncode=0,
            stdout='{"status": "registered", "version": "v2.0.0"}'
        )
        
        from backend.services.version_service import VersionClient
        client = VersionClient()
        result = client.register_version("v2.0.0", test_report_path="/path/to/report.json")
        
        args = mock_run.call_args[0][0]
        assert "--test-report" in args

    @patch('subprocess.run')
    @patch('shutil.which')
    def test_register_version_validation(self, mock_which, mock_run):
        """TC-023: 测试版本号格式验证"""
        mock_which.return_value = "/usr/bin/conf-man"
        mock_run.return_value = Mock(
            returncode=0,
            stdout='{"status": "registered", "version": "v2.0.0"}'
        )
        
        from backend.services.version_service import VersionClient
        client = VersionClient()
        
        with pytest.raises(Exception):
            client.register_version("")


class TestVersionClientReleaseV2:
    """测试版本发布V2 - TC-024~TC-025"""

    @patch('subprocess.run')
    @patch('shutil.which')
    def test_release_version_not_found(self, mock_which, mock_run):
        """TC-024: 测试发布不存在的版本"""
        mock_which.return_value = "/usr/bin/conf-man"
        mock_run.return_value = Mock(
            returncode=1,
            stderr="Version not found: v99.0.0"
        )
        
        from backend.services.version_service import VersionClient
        client = VersionClient()
        
        with pytest.raises(RuntimeError) as exc_info:
            client.release_version("v99.0.0")
        
        assert "not found" in str(exc_info.value).lower()

    @patch('subprocess.run')
    @patch('shutil.which')
    def test_release_version_with_target(self, mock_which, mock_run):
        """TC-025: 测试带目标发布版本"""
        mock_which.return_value = "/usr/bin/conf-man"
        mock_run.return_value = Mock(
            returncode=0,
            stdout='{"status": "released", "version": "v1.0.0", "target": "production"}'
        )
        
        from backend.services.version_service import VersionClient
        client = VersionClient()
        
        with patch('backend.services.version_service.VersionClient.release_version', 
                   return_value={"status": "released", "target": "production"}):
            result = client.release_version("v1.0.0")
            assert result["status"] == "released"


class TestVersionClientRollbackV2:
    """测试版本回滚V2 - TC-026"""

    @patch('subprocess.run')
    @patch('shutil.which')
    def test_rollback_version_success(self, mock_which, mock_run):
        """TC-026: 测试回滚版本成功"""
        mock_which.return_value = "/usr/bin/conf-man"
        mock_run.return_value = Mock(
            returncode=0,
            stdout='{"status": "rolledback", "version": "v1.0.0"}'
        )
        
        from backend.services.version_service import VersionClient
        client = VersionClient()
        result = client.rollback_version("v1.0.0")
        
        assert result["status"] == "rolledback"


class TestVersionClientProjectsV2:
    """测试项目管理V2 - TC-027~TC-029"""

    @patch('subprocess.run')
    @patch('shutil.which')
    def test_list_projects_empty(self, mock_which, mock_run):
        """TC-027: 测试项目列表为空"""
        mock_which.return_value = "/usr/bin/conf-man"
        mock_run.return_value = Mock(returncode=0, stdout='[]')
        
        from backend.services.version_service import VersionClient
        client = VersionClient()
        result = client.list_projects()
        
        assert result == []

    @patch('subprocess.run')
    @patch('shutil.which')
    def test_list_projects_with_data(self, mock_which, mock_run):
        """TC-028: 测试项目列表有数据"""
        mock_which.return_value = "/usr/bin/conf-man"
        mock_run.return_value = Mock(
            returncode=0,
            stdout='[{"name": "pm-agent", "versions": 3}]'
        )
        
        from backend.services.version_service import VersionClient
        client = VersionClient()
        result = client.list_projects()
        
        assert len(result) == 1

    @patch('subprocess.run')
    @patch('shutil.which')
    def test_show_project_success(self, mock_which, mock_run):
        """TC-029: 测试显示项目详情"""
        mock_which.return_value = "/usr/bin/conf-man"
        mock_run.return_value = Mock(
            returncode=0,
            stdout='{"name": "pm-agent", "versions": ["v1.0.0", "v1.1.0"]}'
        )
        
        from backend.services.version_service import VersionClient
        client = VersionClient()
        result = client.show_project("pm-agent")
        
        assert result["name"] == "pm-agent"


class TestVersionClientManifestV2:
    """测试清单导入导出V2 - TC-030~TC-031"""

    @patch('subprocess.run')
    @patch('shutil.which')
    def test_export_manifest_success(self, mock_which, mock_run):
        """TC-030: 测试导出版本清单"""
        mock_which.return_value = "/usr/bin/conf-man"
        mock_run.return_value = Mock(returncode=0, stdout='{"status": "exported"}')
        
        from backend.services.version_service import VersionClient
        client = VersionClient()
        result = client.export_manifest("v1.0.0", "/output/path")
        
        args = mock_run.call_args[0][0]
        assert "export" in args

    @patch('subprocess.run')
    @patch('shutil.which')
    def test_import_manifest_success(self, mock_which, mock_run):
        """TC-031: 测试导入版本清单"""
        mock_which.return_value = "/usr/bin/conf-man"
        mock_run.return_value = Mock(returncode=0, stdout='{"status": "imported"}')
        
        from backend.services.version_service import VersionClient
        client = VersionClient()
        result = client.import_manifest("/input/path")
        
        args = mock_run.call_args[0][0]
        assert "import" in args


class TestVersionClientEdgeCasesV2:
    """测试边界条件V2 - TC-032~TC-035"""

    @patch('subprocess.run')
    @patch('shutil.which')
    def test_command_timeout(self, mock_which, mock_run):
        """TC-032: 测试命令超时"""
        mock_which.return_value = "/usr/bin/conf-man"
        mock_run.side_effect = subprocess.TimeoutExpired("conf-man", 30)
        
        from backend.services.version_service import VersionClient
        client = VersionClient(timeout=30)
        
        with pytest.raises(TimeoutError):
            client.list_versions()

    @patch('subprocess.run')
    @patch('shutil.which')
    def test_invalid_json_response(self, mock_which, mock_run):
        """TC-033: 测试无效JSON响应"""
        mock_which.return_value = "/usr/bin/conf-man"
        mock_run.return_value = Mock(returncode=0, stdout='not valid json')
        
        from backend.services.version_service import VersionClient
        client = VersionClient()
        result = client.list_versions()
        
        assert isinstance(result, dict)

    @patch('subprocess.run')
    @patch('shutil.which')
    def test_cli_not_found_error(self, mock_which, mock_run):
        """TC-034: 测试CLI未找到错误"""
        mock_which.return_value = None
        
        from backend.services.version_service import VersionClient
        client = VersionClient()
        
        with pytest.raises(RuntimeError) as exc_info:
            client.list_versions()
        
        assert "not found" in str(exc_info.value).lower()

    @patch('subprocess.run')
    @patch('shutil.which')
    def test_empty_version_string_direct(self, mock_which, mock_run):
        """TC-035: 测试空版本字符串直接调用"""
        mock_which.return_value = "/usr/bin/conf-man"
        mock_run.return_value = Mock(returncode=0, stdout='{}')
        
        from backend.services.version_service import VersionClient
        client = VersionClient()
        
        with pytest.raises(Exception):
            client.show_version("")


if __name__ == "__main__":
    pytest.main([__file__, "-v"])
