"""
Comprehensive tests for rhoshift.utils.stability_coordinator module.
"""

import pytest
from unittest.mock import patch, Mock, call
from rhoshift.utils.stability_coordinator import (
    StabilityLevel,
    StabilityConfig,
    StabilityCoordinator
)


class TestStabilityLevel:
    """Test cases for StabilityLevel enum"""
    
    def test_stability_level_values(self):
        """Test StabilityLevel enum values and ordering"""
        assert StabilityLevel.BASIC.value == 1
        assert StabilityLevel.ENHANCED.value == 2
        assert StabilityLevel.COMPREHENSIVE.value == 3
        
        # Test ordering
        assert StabilityLevel.BASIC < StabilityLevel.ENHANCED
        assert StabilityLevel.ENHANCED < StabilityLevel.COMPREHENSIVE
        assert StabilityLevel.COMPREHENSIVE > StabilityLevel.BASIC
    
    def test_stability_level_string_representation(self):
        """Test StabilityLevel string representation"""
        assert str(StabilityLevel.BASIC) == "StabilityLevel.BASIC"
        assert str(StabilityLevel.ENHANCED) == "StabilityLevel.ENHANCED"
        assert str(StabilityLevel.COMPREHENSIVE) == "StabilityLevel.COMPREHENSIVE"


class TestStabilityConfig:
    """Test cases for StabilityConfig dataclass"""
    
    def test_stability_config_default(self):
        """Test StabilityConfig with default values"""
        config = StabilityConfig()
        
        assert config.level == StabilityLevel.ENHANCED
        assert config.enable_preflight_checks is True
        assert config.enable_health_monitoring is True
        assert config.enable_auto_recovery is True
        assert config.max_retries == 3
        assert config.retry_delay == 10
        assert config.timeout == 300
        assert config.health_check_interval == 30
        assert config.recovery_attempts == 2
    
    def test_stability_config_custom(self):
        """Test StabilityConfig with custom values"""
        config = StabilityConfig(
            level=StabilityLevel.COMPREHENSIVE,
            enable_preflight_checks=False,
            max_retries=5,
            retry_delay=15,
            timeout=600,
            health_check_interval=60,
            recovery_attempts=3
        )
        
        assert config.level == StabilityLevel.COMPREHENSIVE
        assert config.enable_preflight_checks is False
        assert config.max_retries == 5
        assert config.retry_delay == 15
        assert config.timeout == 600
        assert config.health_check_interval == 60
        assert config.recovery_attempts == 3
    
    def test_stability_config_basic_level(self):
        """Test StabilityConfig with basic stability level"""
        config = StabilityConfig(level=StabilityLevel.BASIC)
        
        assert config.level == StabilityLevel.BASIC
        # Basic level might have different defaults
        assert isinstance(config.enable_preflight_checks, bool)
        assert isinstance(config.max_retries, int)


class TestOperatorInstallationResult:
    """Test cases for OperatorInstallationResult dataclass"""
    
    def test_operator_installation_result_success(self):
        """Test OperatorInstallationResult for successful installation"""
        result = OperatorInstallationResult(
            operator_name="test-operator",
            success=True,
            return_code=0,
            stdout="Installation successful",
            stderr="",
            duration=120.5,
            retry_count=0,
            health_status="Healthy",
            warnings=[]
        )
        
        assert result.operator_name == "test-operator"
        assert result.success is True
        assert result.return_code == 0
        assert result.stdout == "Installation successful"
        assert result.stderr == ""
        assert result.duration == 120.5
        assert result.retry_count == 0
        assert result.health_status == "Healthy"
        assert result.warnings == []
    
    def test_operator_installation_result_failure(self):
        """Test OperatorInstallationResult for failed installation"""
        result = OperatorInstallationResult(
            operator_name="failing-operator",
            success=False,
            return_code=1,
            stdout="",
            stderr="Installation failed",
            duration=45.2,
            retry_count=3,
            health_status="Unhealthy",
            warnings=["Warning: deprecated API", "Warning: resource limit"]
        )
        
        assert result.operator_name == "failing-operator"
        assert result.success is False
        assert result.return_code == 1
        assert result.stderr == "Installation failed"
        assert result.retry_count == 3
        assert result.health_status == "Unhealthy"
        assert len(result.warnings) == 2


class TestStabilityCoordinator:
    """Test cases for StabilityCoordinator class"""
    
    def test_stability_coordinator_init_default(self):
        """Test StabilityCoordinator initialization with defaults"""
        coordinator = StabilityCoordinator()
        
        assert coordinator.config.level == StabilityLevel.ENHANCED
        assert coordinator.oc_binary == "oc"
        assert isinstance(coordinator.installation_results, list)
        assert len(coordinator.installation_results) == 0
    
    def test_stability_coordinator_init_custom(self):
        """Test StabilityCoordinator initialization with custom config"""
        config = StabilityConfig(level=StabilityLevel.COMPREHENSIVE, max_retries=5)
        coordinator = StabilityCoordinator(config, "/custom/oc")
        
        assert coordinator.config.level == StabilityLevel.COMPREHENSIVE
        assert coordinator.config.max_retries == 5
        assert coordinator.oc_binary == "/custom/oc"
    
    @patch('rhoshift.utils.resilience.run_preflight_checks')
    def test_run_preflight_checks_enabled(self, mock_preflight):
        """Test running preflight checks when enabled"""
        mock_preflight.return_value = (True, [])
        
        config = StabilityConfig(enable_preflight_checks=True)
        coordinator = StabilityCoordinator(config)
        
        success, warnings = coordinator.run_preflight_checks()
        
        assert success is True
        assert warnings == []
        mock_preflight.assert_called_once_with("oc")
    
    @patch('rhoshift.utils.resilience.run_preflight_checks')
    def test_run_preflight_checks_disabled(self, mock_preflight):
        """Test running preflight checks when disabled"""
        config = StabilityConfig(enable_preflight_checks=False)
        coordinator = StabilityCoordinator(config)
        
        success, warnings = coordinator.run_preflight_checks()
        
        assert success is True
        assert warnings == []
        mock_preflight.assert_not_called()
    
    @patch('rhoshift.utils.health_monitor.check_operator_health')
    def test_monitor_operator_health_enabled(self, mock_health_check):
        """Test monitoring operator health when enabled"""
        from rhoshift.utils.health_monitor import HealthStatus
        mock_health_check.return_value = (HealthStatus.HEALTHY, {'status': 'good'})
        
        config = StabilityConfig(enable_health_monitoring=True)
        coordinator = StabilityCoordinator(config)
        
        status, results = coordinator.monitor_operator_health("test-operator", "test-namespace")
        
        assert status == HealthStatus.HEALTHY
        assert results['status'] == 'good'
        mock_health_check.assert_called_once_with("test-operator", "test-namespace", "oc")
    
    @patch('rhoshift.utils.health_monitor.check_operator_health')
    def test_monitor_operator_health_disabled(self, mock_health_check):
        """Test monitoring operator health when disabled"""
        from rhoshift.utils.health_monitor import HealthStatus
        
        config = StabilityConfig(enable_health_monitoring=False)
        coordinator = StabilityCoordinator(config)
        
        status, results = coordinator.monitor_operator_health("test-operator", "test-namespace")
        
        assert status == HealthStatus.UNKNOWN
        assert results == {}
        mock_health_check.assert_not_called()
    
    @patch('rhoshift.utils.resilience.execute_resilient_operation')
    @patch('time.time')
    def test_install_operator_with_stability_success(self, mock_time, mock_resilient_op):
        """Test operator installation with stability features - success"""
        mock_time.side_effect = [1000.0, 1120.5]  # Start and end times
        mock_resilient_op.return_value = (True, (0, "success", ""), [])
        
        coordinator = StabilityCoordinator()
        
        def mock_installation(**kwargs):
            return (0, "Installation successful", "")
        
        success, results = coordinator.install_operator_with_stability(
            operator_name="test-operator",
            namespace="test-namespace",
            installation_func=mock_installation
        )
        
        assert success is True
        assert 'test-operator' in results
        
        # Check that result was recorded
        assert len(coordinator.installation_results) == 1
        result = coordinator.installation_results[0]
        assert result.operator_name == "test-operator"
        assert result.success is True
        assert result.duration == 120.5
    
    @patch('rhoshift.utils.resilience.execute_resilient_operation')
    @patch('time.time')
    def test_install_operator_with_stability_failure(self, mock_time, mock_resilient_op):
        """Test operator installation with stability features - failure"""
        mock_time.side_effect = [1000.0, 1045.2]  # Start and end times
        mock_resilient_op.return_value = (False, "Installation failed", ["Error occurred"])
        
        coordinator = StabilityCoordinator()
        
        def mock_installation(**kwargs):
            raise Exception("Installation failed")
        
        success, results = coordinator.install_operator_with_stability(
            operator_name="failing-operator",
            namespace="test-namespace",
            installation_func=mock_installation
        )
        
        assert success is False
        assert 'failing-operator' in results
        assert 'errors' in results
        
        # Check that result was recorded
        assert len(coordinator.installation_results) == 1
        result = coordinator.installation_results[0]
        assert result.operator_name == "failing-operator"
        assert result.success is False
        assert result.duration == 45.2
    
    @patch('rhoshift.utils.health_monitor.monitor_operator_installation')
    def test_install_operator_with_health_monitoring(self, mock_monitor):
        """Test operator installation with health monitoring"""
        from rhoshift.utils.health_monitor import HealthStatus
        mock_monitor.return_value = (True, HealthStatus.HEALTHY, {'status': 'ready'})
        
        config = StabilityConfig(enable_health_monitoring=True)
        coordinator = StabilityCoordinator(config)
        
        def mock_installation(**kwargs):
            return (0, "Installation successful", "")
        
        success, results = coordinator.install_operator_with_stability(
            operator_name="test-operator",
            namespace="test-namespace",
            installation_func=mock_installation
        )
        
        assert success is True
        mock_monitor.assert_called_once()
    
    def test_record_installation_result(self):
        """Test recording installation results"""
        coordinator = StabilityCoordinator()
        
        result = OperatorInstallationResult(
            operator_name="test-operator",
            success=True,
            return_code=0,
            stdout="success",
            stderr="",
            duration=60.0,
            retry_count=1,
            health_status="Healthy",
            warnings=[]
        )
        
        coordinator.record_installation_result(result)
        
        assert len(coordinator.installation_results) == 1
        assert coordinator.installation_results[0] == result
    
    def test_get_installation_summary(self):
        """Test getting installation summary"""
        coordinator = StabilityCoordinator()
        
        # Add some results
        results = [
            OperatorInstallationResult(
                operator_name="op1", success=True, return_code=0, stdout="", stderr="",
                duration=60.0, retry_count=0, health_status="Healthy", warnings=[]
            ),
            OperatorInstallationResult(
                operator_name="op2", success=False, return_code=1, stdout="", stderr="error",
                duration=30.0, retry_count=3, health_status="Unhealthy", warnings=["warning"]
            ),
            OperatorInstallationResult(
                operator_name="op3", success=True, return_code=0, stdout="", stderr="",
                duration=45.0, retry_count=1, health_status="Healthy", warnings=[]
            )
        ]
        
        for result in results:
            coordinator.record_installation_result(result)
        
        summary = coordinator.get_installation_summary()
        
        assert summary['total_operators'] == 3
        assert summary['successful'] == 2
        assert summary['failed'] == 1
        assert summary['success_rate'] == pytest.approx(0.667, rel=1e-2)
        assert summary['total_duration'] == 135.0
        assert summary['average_duration'] == 45.0
        assert summary['total_retries'] == 4
    
    def test_generate_stability_report(self):
        """Test generating stability report"""
        coordinator = StabilityCoordinator()
        
        # Add a result
        result = OperatorInstallationResult(
            operator_name="test-operator",
            success=True,
            return_code=0,
            stdout="success",
            stderr="",
            duration=90.0,
            retry_count=2,
            health_status="Healthy",
            warnings=["minor warning"]
        )
        coordinator.record_installation_result(result)
        
        report = coordinator.generate_stability_report()
        
        assert isinstance(report, str)
        assert "STABILITY COORDINATOR REPORT" in report
        assert "test-operator" in report
        assert "Healthy" in report
        assert "90.0" in report
    
    def test_monitor_installed_operators(self):
        """Test monitoring all installed operators"""
        coordinator = StabilityCoordinator()
        
        # Add some installation results
        results = [
            OperatorInstallationResult(
                operator_name="op1", success=True, return_code=0, stdout="", stderr="",
                duration=60.0, retry_count=0, health_status="Healthy", warnings=[]
            ),
            OperatorInstallationResult(
                operator_name="op2", success=True, return_code=0, stdout="", stderr="",
                duration=45.0, retry_count=0, health_status="Healthy", warnings=[]
            )
        ]
        
        for result in results:
            coordinator.record_installation_result(result)
        
        with patch.object(coordinator, 'monitor_operator_health') as mock_monitor:
            from rhoshift.utils.health_monitor import HealthStatus
            mock_monitor.return_value = (HealthStatus.HEALTHY, {'status': 'good'})
            
            monitoring_results = coordinator.monitor_installed_operators()
            
            assert len(monitoring_results) == 2
            assert 'op1' in monitoring_results
            assert 'op2' in monitoring_results
            assert mock_monitor.call_count == 2


class TestStabilityIntegration:
    """Integration tests for stability coordinator features"""
    
    @patch('rhoshift.utils.resilience.run_preflight_checks')
    @patch('rhoshift.utils.health_monitor.monitor_operator_installation')
    @patch('time.time')
    def test_complete_stability_workflow(self, mock_time, mock_monitor, mock_preflight):
        """Test complete stability coordination workflow"""
        from rhoshift.utils.health_monitor import HealthStatus
        
        # Setup mocks
        mock_time.side_effect = [1000.0, 1150.0]
        mock_preflight.return_value = (True, [])
        mock_monitor.return_value = (True, HealthStatus.HEALTHY, {'status': 'ready'})
        
        config = StabilityConfig(level=StabilityLevel.COMPREHENSIVE)
        coordinator = StabilityCoordinator(config)
        
        # Run preflight checks
        preflight_success, preflight_warnings = coordinator.run_preflight_checks()
        assert preflight_success is True
        
        # Install operator with stability
        def mock_installation(**kwargs):
            return (0, "Installation successful", "")
        
        success, results = coordinator.install_operator_with_stability(
            operator_name="comprehensive-operator",
            namespace="test-namespace",
            installation_func=mock_installation
        )
        
        assert success is True
        
        # Verify all components were called
        mock_preflight.assert_called_once()
        mock_monitor.assert_called_once()
        
        # Check recorded results
        assert len(coordinator.installation_results) == 1
        result = coordinator.installation_results[0]
        assert result.operator_name == "comprehensive-operator"
        assert result.success is True
        
        # Generate report
        report = coordinator.generate_stability_report()
        assert "comprehensive-operator" in report
        assert "COMPREHENSIVE" in report.upper()
    
    @patch('rhoshift.utils.resilience.execute_resilient_operation')
    def test_stability_coordinator_with_recovery(self, mock_resilient_op):
        """Test stability coordinator with recovery actions"""
        # Simulate recovery scenario
        mock_resilient_op.return_value = (True, (0, "success after recovery", ""), ["Recovery attempted"])
        
        coordinator = StabilityCoordinator()
        
        def failing_then_succeeding_installation(**kwargs):
            # This would normally fail first, then succeed after recovery
            return (0, "success after recovery", "")
        
        success, results = coordinator.install_operator_with_stability(
            operator_name="recovery-operator",
            namespace="test-namespace",
            installation_func=failing_then_succeeding_installation
        )
        
        assert success is True
        mock_resilient_op.assert_called_once()
        
        # Check that warnings from recovery are included
        result = coordinator.installation_results[0]
        assert result.operator_name == "recovery-operator"
        assert result.success is True
    
    def test_stability_levels_affect_behavior(self):
        """Test that different stability levels affect coordinator behavior"""
        basic_config = StabilityConfig(level=StabilityLevel.BASIC)
        enhanced_config = StabilityConfig(level=StabilityLevel.ENHANCED)
        comprehensive_config = StabilityConfig(level=StabilityLevel.COMPREHENSIVE)
        
        basic_coordinator = StabilityCoordinator(basic_config)
        enhanced_coordinator = StabilityCoordinator(enhanced_config)
        comprehensive_coordinator = StabilityCoordinator(comprehensive_config)
        
        # All should be properly initialized
        assert basic_coordinator.config.level == StabilityLevel.BASIC
        assert enhanced_coordinator.config.level == StabilityLevel.ENHANCED
        assert comprehensive_coordinator.config.level == StabilityLevel.COMPREHENSIVE
        
        # Different levels might have different default behaviors
        # This is mainly testing that the different levels can be instantiated
        # and that the configuration is properly set
        assert basic_coordinator.config.level.value < enhanced_coordinator.config.level.value
        assert enhanced_coordinator.config.level.value < comprehensive_coordinator.config.level.value
