"""Unit tests for interactive optimization dashboard."""

import pytest
import json
import time
from unittest.mock import Mock, patch, MagicMock
from typing import Dict, List, Any
import tempfile
import os
from pathlib import Path

from src.services.optimization_dashboard import (
    OptimizationDashboard,
    RealTimeMonitor,
    InteractiveCharts,
    ConfigurationManager,
    AlertSystem,
    DashboardComponent
)


class TestOptimizationDashboard:
    """Test the main OptimizationDashboard class."""
    
    def test_dashboard_initialization(self):
        """Test OptimizationDashboard initialization with default settings."""
        dashboard = OptimizationDashboard()
        
        assert dashboard.port == 8080
        assert dashboard.host == 'localhost'
        assert dashboard.auto_refresh_seconds == 5
        assert dashboard.max_data_points == 1000
        assert dashboard.enable_websockets is True
    
    def test_dashboard_custom_initialization(self):
        """Test OptimizationDashboard initialization with custom settings."""
        dashboard = OptimizationDashboard(
            port=9000,
            host='0.0.0.0',
            auto_refresh_seconds=10,
            max_data_points=500,
            enable_websockets=False
        )
        
        assert dashboard.port == 9000
        assert dashboard.host == '0.0.0.0'
        assert dashboard.auto_refresh_seconds == 10
        assert dashboard.max_data_points == 500
        assert dashboard.enable_websockets is False
    
    def test_start_dashboard(self):
        """Test dashboard startup."""
        dashboard = OptimizationDashboard()
        
        # Mock the web server
        with patch('src.services.optimization_dashboard.threading.Thread') as mock_thread:
            mock_thread.return_value.start.return_value = None
            
            result = dashboard.start_dashboard()
            
            assert result['status'] == 'started'
            assert result['port'] == 8080
            assert result['url'] == 'http://localhost:8080'
    
    def test_stop_dashboard(self):
        """Test dashboard shutdown."""
        dashboard = OptimizationDashboard()
        
        # Start dashboard first
        dashboard.start_dashboard()
        
        result = dashboard.stop_dashboard()
        
        assert result['status'] == 'stopped'
        assert 'shutdown_time' in result
    
    def test_get_dashboard_status(self):
        """Test dashboard status retrieval."""
        dashboard = OptimizationDashboard()
        
        result = dashboard.get_dashboard_status()
        
        assert 'status' in result
        assert 'uptime_seconds' in result
        assert 'active_connections' in result
        assert 'data_points_count' in result
    
    def test_update_metrics(self):
        """Test metrics update."""
        dashboard = OptimizationDashboard()
        
        metrics = {
            'cpu_usage': 75.5,
            'memory_usage': 60.2,
            'throughput': 25.0,
            'latency': 0.15
        }
        
        result = dashboard.update_metrics(metrics)
        
        assert result['updated'] is True
        assert result['timestamp'] > 0
        assert result['metrics_count'] == 4


class TestRealTimeMonitor:
    """Test real-time monitoring functionality."""
    
    def test_real_time_monitor_initialization(self):
        """Test RealTimeMonitor initialization."""
        monitor = RealTimeMonitor()
        
        assert monitor.update_interval_seconds == 1
        assert monitor.max_history_size == 1000
        assert monitor.enable_alerts is True
        assert monitor.metrics_enabled == ['cpu', 'memory', 'throughput', 'latency']
    
    def test_real_time_monitor_custom_initialization(self):
        """Test RealTimeMonitor initialization with custom settings."""
        monitor = RealTimeMonitor(
            update_interval_seconds=2,
            max_history_size=500,
            enable_alerts=False,
            metrics_enabled=['cpu', 'memory']
        )
        
        assert monitor.update_interval_seconds == 2
        assert monitor.max_history_size == 500
        assert monitor.enable_alerts is False
        assert monitor.metrics_enabled == ['cpu', 'memory']
    
    def test_start_monitoring(self):
        """Test monitoring start."""
        monitor = RealTimeMonitor()
        
        result = monitor.start_monitoring()
        
        assert result['status'] == 'started'
        assert result['update_interval'] == 1
        assert result['metrics_enabled'] == ['cpu', 'memory', 'throughput', 'latency']
    
    def test_stop_monitoring(self):
        """Test monitoring stop."""
        monitor = RealTimeMonitor()
        
        # Start monitoring first
        monitor.start_monitoring()
        
        result = monitor.stop_monitoring()
        
        assert result['status'] == 'stopped'
        assert 'stop_time' in result
    
    def test_collect_metrics(self):
        """Test metrics collection."""
        monitor = RealTimeMonitor()
        
        # Mock system metrics
        with patch('psutil.cpu_percent') as mock_cpu:
            with patch('psutil.virtual_memory') as mock_memory:
                mock_cpu.return_value = 75.5
                mock_memory.return_value = Mock(percent=60.2)
                
                metrics = monitor.collect_metrics()
                
                assert 'cpu_usage' in metrics
                assert 'memory_usage' in metrics
                assert 'timestamp' in metrics
                assert metrics['cpu_usage'] == 75.5
                assert metrics['memory_usage'] == 60.2
    
    def test_get_metrics_history(self):
        """Test metrics history retrieval."""
        monitor = RealTimeMonitor()
        
        # Add some test data
        monitor.metrics_history = [
            {'timestamp': 1000, 'cpu_usage': 50.0, 'memory_usage': 40.0},
            {'timestamp': 2000, 'cpu_usage': 60.0, 'memory_usage': 50.0},
            {'timestamp': 3000, 'cpu_usage': 70.0, 'memory_usage': 60.0}
        ]
        
        history = monitor.get_metrics_history()
        
        assert len(history) == 3
        assert all('timestamp' in metric for metric in history)
        assert all('cpu_usage' in metric for metric in history)
    
    def test_get_metrics_summary(self):
        """Test metrics summary generation."""
        monitor = RealTimeMonitor()
        
        # Add test data
        monitor.metrics_history = [
            {'timestamp': 1000, 'cpu_usage': 50.0, 'memory_usage': 40.0},
            {'timestamp': 2000, 'cpu_usage': 60.0, 'memory_usage': 50.0},
            {'timestamp': 3000, 'cpu_usage': 70.0, 'memory_usage': 60.0}
        ]
        
        summary = monitor.get_metrics_summary()
        
        assert 'cpu_usage' in summary
        assert 'memory_usage' in summary
        assert 'avg_cpu_usage' in summary['cpu_usage']
        assert 'max_cpu_usage' in summary['cpu_usage']
        assert 'min_cpu_usage' in summary['cpu_usage']
        assert summary['cpu_usage']['avg_cpu_usage'] == 60.0


class TestInteractiveCharts:
    """Test interactive chart functionality."""
    
    def test_interactive_charts_initialization(self):
        """Test InteractiveCharts initialization."""
        charts = InteractiveCharts()
        
        assert charts.chart_types == ['line', 'bar', 'scatter', 'heatmap']
        assert charts.default_chart_type == 'line'
        assert charts.auto_update is True
        assert charts.max_data_points == 1000
    
    def test_interactive_charts_custom_initialization(self):
        """Test InteractiveCharts initialization with custom settings."""
        charts = InteractiveCharts(
            chart_types=['line', 'bar'],
            default_chart_type='bar',
            auto_update=False,
            max_data_points=500
        )
        
        assert charts.chart_types == ['line', 'bar']
        assert charts.default_chart_type == 'bar'
        assert charts.auto_update is False
        assert charts.max_data_points == 500
    
    def test_create_performance_chart(self):
        """Test performance chart creation."""
        charts = InteractiveCharts()
        
        data = [
            {'timestamp': 1000, 'cpu_usage': 50.0, 'memory_usage': 40.0},
            {'timestamp': 2000, 'cpu_usage': 60.0, 'memory_usage': 50.0},
            {'timestamp': 3000, 'cpu_usage': 70.0, 'memory_usage': 60.0}
        ]
        
        chart_config = charts.create_performance_chart(data, 'cpu_usage')
        
        assert 'chart_type' in chart_config
        assert 'data' in chart_config
        assert 'options' in chart_config
        assert chart_config['chart_type'] == 'line'
        assert len(chart_config['data']) == 3
    
    def test_create_throughput_chart(self):
        """Test throughput chart creation."""
        charts = InteractiveCharts()
        
        data = [
            {'timestamp': 1000, 'throughput': 10.0, 'latency': 0.1},
            {'timestamp': 2000, 'throughput': 15.0, 'latency': 0.15},
            {'timestamp': 3000, 'throughput': 20.0, 'latency': 0.2}
        ]
        
        chart_config = charts.create_throughput_chart(data)
        
        assert 'chart_type' in chart_config
        assert 'data' in chart_config
        assert 'options' in chart_config
        assert chart_config['chart_type'] == 'line'
        assert len(chart_config['data']) == 3
    
    def test_create_memory_chart(self):
        """Test memory chart creation."""
        charts = InteractiveCharts()
        
        data = [
            {'timestamp': 1000, 'memory_usage': 40.0, 'gpu_memory': 30.0},
            {'timestamp': 2000, 'memory_usage': 50.0, 'gpu_memory': 40.0},
            {'timestamp': 3000, 'memory_usage': 60.0, 'gpu_memory': 50.0}
        ]
        
        chart_config = charts.create_memory_chart(data)
        
        assert 'chart_type' in chart_config
        assert 'data' in chart_config
        assert 'options' in chart_config
        assert chart_config['chart_type'] == 'line'
        assert len(chart_config['data']) == 3
    
    def test_create_combined_chart(self):
        """Test combined chart creation."""
        charts = InteractiveCharts()
        
        data = [
            {'timestamp': 1000, 'cpu_usage': 50.0, 'memory_usage': 40.0, 'throughput': 10.0},
            {'timestamp': 2000, 'cpu_usage': 60.0, 'memory_usage': 50.0, 'throughput': 15.0},
            {'timestamp': 3000, 'cpu_usage': 70.0, 'memory_usage': 60.0, 'throughput': 20.0}
        ]
        
        chart_config = charts.create_combined_chart(data)
        
        assert 'chart_type' in chart_config
        assert 'data' in chart_config
        assert 'options' in chart_config
        assert chart_config['chart_type'] == 'line'
        assert len(chart_config['data']) == 3
    
    def test_export_chart_data(self):
        """Test chart data export."""
        charts = InteractiveCharts()
        
        data = [
            {'timestamp': 1000, 'cpu_usage': 50.0, 'memory_usage': 40.0},
            {'timestamp': 2000, 'cpu_usage': 60.0, 'memory_usage': 50.0}
        ]
        
        # Test JSON export
        json_data = charts.export_chart_data(data, 'json')
        assert json_data['format'] == 'json'
        assert len(json_data['data']) == 2
        
        # Test CSV export
        csv_data = charts.export_chart_data(data, 'csv')
        assert csv_data['format'] == 'csv'
        assert 'data' in csv_data


class TestConfigurationManager:
    """Test configuration management functionality."""
    
    def test_configuration_manager_initialization(self):
        """Test ConfigurationManager initialization."""
        config_manager = ConfigurationManager()
        
        assert config_manager.config_file == 'dashboard_config.json'
        assert config_manager.auto_save is True
        assert config_manager.backup_enabled is True
        assert config_manager.max_backups == 5
    
    def test_configuration_manager_custom_initialization(self):
        """Test ConfigurationManager initialization with custom settings."""
        config_manager = ConfigurationManager(
            config_file='custom_config.json',
            auto_save=False,
            backup_enabled=False,
            max_backups=3
        )
        
        assert config_manager.config_file == 'custom_config.json'
        assert config_manager.auto_save is False
        assert config_manager.backup_enabled is False
        assert config_manager.max_backups == 3
    
    def test_load_configuration(self):
        """Test configuration loading."""
        config_manager = ConfigurationManager()
        
        # Mock file reading
        mock_config = {
            'dashboard': {'port': 8080, 'host': 'localhost'},
            'monitoring': {'interval': 1, 'enabled': True},
            'charts': {'auto_update': True, 'max_points': 1000}
        }
        
        with patch('builtins.open', create=True) as mock_open:
            mock_open.return_value.__enter__.return_value.read.return_value = json.dumps(mock_config)
            
            config = config_manager.load_configuration()
            
            assert config['dashboard']['port'] == 8080
            assert config['monitoring']['interval'] == 1
            assert config['charts']['auto_update'] is True
    
    def test_save_configuration(self):
        """Test configuration saving."""
        config_manager = ConfigurationManager()
        
        config = {
            'dashboard': {'port': 9000, 'host': '0.0.0.0'},
            'monitoring': {'interval': 2, 'enabled': True},
            'charts': {'auto_update': False, 'max_points': 500}
        }
        
        with patch('builtins.open', create=True) as mock_open:
            mock_open.return_value.__enter__.return_value.write.return_value = None
            
            result = config_manager.save_configuration(config)
            
            assert result['saved'] is True
            assert result['timestamp'] > 0
    
    def test_update_configuration(self):
        """Test configuration update."""
        config_manager = ConfigurationManager()
        
        current_config = {
            'dashboard': {'port': 8080, 'host': 'localhost'},
            'monitoring': {'interval': 1, 'enabled': True}
        }
        
        updates = {
            'dashboard': {'port': 9000},
            'monitoring': {'interval': 2}
        }
        
        new_config = config_manager.update_configuration(current_config, updates)
        
        assert new_config['dashboard']['port'] == 9000
        assert new_config['dashboard']['host'] == 'localhost'  # Unchanged
        assert new_config['monitoring']['interval'] == 2
        assert new_config['monitoring']['enabled'] is True  # Unchanged
    
    def test_validate_configuration(self):
        """Test configuration validation."""
        config_manager = ConfigurationManager()
        
        valid_config = {
            'dashboard': {'port': 8080, 'host': 'localhost'},
            'monitoring': {'interval': 1, 'enabled': True},
            'charts': {'auto_update': True, 'max_points': 1000}
        }
        
        result = config_manager.validate_configuration(valid_config)
        
        assert result['valid'] is True
        assert result['errors'] is None
        
        # Test invalid config
        invalid_config = {
            'dashboard': {'port': -1},  # Invalid port
            'monitoring': {'interval': 0}  # Invalid interval
        }
        
        result = config_manager.validate_configuration(invalid_config)
        
        assert result['valid'] is False
        assert 'errors' in result
        assert len(result['errors']) > 0
    
    def test_create_backup(self):
        """Test configuration backup creation."""
        config_manager = ConfigurationManager()
        
        config = {'test': 'data'}
        
        with patch('builtins.open', create=True) as mock_open:
            mock_open.return_value.__enter__.return_value.write.return_value = None
            
            result = config_manager.create_backup(config)
            
            assert result['backup_created'] is True
            assert 'backup_file' in result
            assert result['timestamp'] > 0


class TestAlertSystem:
    """Test alert system functionality."""
    
    def test_alert_system_initialization(self):
        """Test AlertSystem initialization."""
        alert_system = AlertSystem()
        
        assert alert_system.enable_alerts is True
        assert alert_system.alert_levels == ['info', 'warning', 'error', 'critical']
        assert alert_system.max_alerts == 100
        assert alert_system.auto_clear is True
    
    def test_alert_system_custom_initialization(self):
        """Test AlertSystem initialization with custom settings."""
        alert_system = AlertSystem(
            enable_alerts=False,
            alert_levels=['warning', 'error'],
            max_alerts=50,
            auto_clear=False
        )
        
        assert alert_system.enable_alerts is False
        assert alert_system.alert_levels == ['warning', 'error']
        assert alert_system.max_alerts == 50
        assert alert_system.auto_clear is False
    
    def test_create_alert(self):
        """Test alert creation."""
        alert_system = AlertSystem()
        
        alert = alert_system.create_alert(
            level='warning',
            message='High CPU usage detected',
            source='monitoring',
            threshold=80.0,
            current_value=85.0
        )
        
        assert alert['level'] == 'warning'
        assert alert['message'] == 'High CPU usage detected'
        assert alert['source'] == 'monitoring'
        assert alert['threshold'] == 80.0
        assert alert['current_value'] == 85.0
        assert 'timestamp' in alert
        assert 'id' in alert
    
    def test_get_alerts(self):
        """Test alert retrieval."""
        alert_system = AlertSystem()
        
        # Add some test alerts
        alert_system.alerts = [
            {'id': '1', 'level': 'info', 'message': 'Test 1', 'timestamp': 1000},
            {'id': '2', 'level': 'warning', 'message': 'Test 2', 'timestamp': 2000},
            {'id': '3', 'level': 'error', 'message': 'Test 3', 'timestamp': 3000}
        ]
        
        all_alerts = alert_system.get_alerts()
        assert len(all_alerts) == 3
        
        warning_alerts = alert_system.get_alerts(level='warning')
        assert len(warning_alerts) == 1
        assert warning_alerts[0]['level'] == 'warning'
    
    def test_clear_alerts(self):
        """Test alert clearing."""
        alert_system = AlertSystem()
        
        # Add test alerts
        alert_system.alerts = [
            {'id': '1', 'level': 'info', 'message': 'Test 1', 'timestamp': 1000},
            {'id': '2', 'level': 'warning', 'message': 'Test 2', 'timestamp': 2000}
        ]
        
        result = alert_system.clear_alerts()
        
        assert result['cleared'] is True
        assert result['count'] == 2
        assert len(alert_system.alerts) == 0
    
    def test_check_thresholds(self):
        """Test threshold checking."""
        alert_system = AlertSystem()
        
        metrics = {
            'cpu_usage': 85.0,
            'memory_usage': 70.0,
            'throughput': 15.0
        }
        
        thresholds = {
            'cpu_usage': {'warning': 80.0, 'error': 90.0},
            'memory_usage': {'warning': 75.0, 'error': 85.0},
            'throughput': {'warning': 10.0, 'error': 5.0}
        }
        
        alerts = alert_system.check_thresholds(metrics, thresholds)
        
        assert len(alerts) > 0
        assert any(alert['level'] == 'warning' for alert in alerts)
    
    def test_get_alert_summary(self):
        """Test alert summary generation."""
        alert_system = AlertSystem()
        
        # Add test alerts
        alert_system.alerts = [
            {'id': '1', 'level': 'info', 'message': 'Test 1', 'timestamp': 1000},
            {'id': '2', 'level': 'warning', 'message': 'Test 2', 'timestamp': 2000},
            {'id': '3', 'level': 'warning', 'message': 'Test 3', 'timestamp': 3000},
            {'id': '4', 'level': 'error', 'message': 'Test 4', 'timestamp': 4000}
        ]
        
        summary = alert_system.get_alert_summary()
        
        assert summary['total_alerts'] == 4
        assert summary['info_count'] == 1
        assert summary['warning_count'] == 2
        assert summary['error_count'] == 1
        assert summary['critical_count'] == 0


class TestDashboardComponent:
    """Test dashboard component functionality."""
    
    def test_dashboard_component_initialization(self):
        """Test DashboardComponent initialization."""
        component = DashboardComponent('test_component')
        
        assert component.name == 'test_component'
        assert component.enabled is True
        assert component.auto_refresh is True
        assert component.refresh_interval == 5
    
    def test_dashboard_component_custom_initialization(self):
        """Test DashboardComponent initialization with custom settings."""
        component = DashboardComponent(
            'test_component',
            enabled=False,
            auto_refresh=False,
            refresh_interval=10
        )
        
        assert component.name == 'test_component'
        assert component.enabled is False
        assert component.auto_refresh is False
        assert component.refresh_interval == 10
    
    def test_component_update(self):
        """Test component update."""
        component = DashboardComponent('test_component')
        
        data = {'value': 42, 'status': 'active'}
        
        result = component.update(data)
        
        assert result['updated'] is True
        assert result['timestamp'] > 0
        assert result['data'] == data
    
    def test_component_get_status(self):
        """Test component status retrieval."""
        component = DashboardComponent('test_component')
        
        status = component.get_status()
        
        assert status['name'] == 'test_component'
        assert status['enabled'] is True
        assert status['auto_refresh'] is True
        assert 'last_update' in status
    
    def test_component_enable_disable(self):
        """Test component enable/disable."""
        component = DashboardComponent('test_component')
        
        # Test disable
        result = component.disable()
        assert result['enabled'] is False
        assert component.enabled is False
        
        # Test enable
        result = component.enable()
        assert result['enabled'] is True
        assert component.enabled is True
    
    def test_component_set_refresh_interval(self):
        """Test component refresh interval setting."""
        component = DashboardComponent('test_component')
        
        result = component.set_refresh_interval(10)
        
        assert result['refresh_interval'] == 10
        assert component.refresh_interval == 10


class TestOptimizationDashboardIntegration:
    """Integration tests for optimization dashboard components."""
    
    def test_dashboard_with_monitoring(self):
        """Test dashboard integration with monitoring."""
        dashboard = OptimizationDashboard()
        monitor = RealTimeMonitor()
        
        # Start monitoring
        monitor_result = monitor.start_monitoring()
        assert monitor_result['status'] == 'started'
        
        # Start dashboard
        dashboard_result = dashboard.start_dashboard()
        assert dashboard_result['status'] == 'started'
        
        # Update metrics
        metrics = {'cpu_usage': 75.0, 'memory_usage': 60.0}
        update_result = dashboard.update_metrics(metrics)
        assert update_result['updated'] is True
        
        # Stop dashboard
        stop_result = dashboard.stop_dashboard()
        assert stop_result['status'] == 'stopped'
    
    def test_dashboard_with_charts(self):
        """Test dashboard integration with charts."""
        dashboard = OptimizationDashboard()
        charts = InteractiveCharts()
        
        # Create test data
        data = [
            {'timestamp': 1000, 'cpu_usage': 50.0, 'memory_usage': 40.0},
            {'timestamp': 2000, 'cpu_usage': 60.0, 'memory_usage': 50.0}
        ]
        
        # Create charts
        performance_chart = charts.create_performance_chart(data, 'cpu_usage')
        memory_chart = charts.create_memory_chart(data)
        
        assert performance_chart['chart_type'] == 'line'
        assert memory_chart['chart_type'] == 'line'
        assert len(performance_chart['data']) == 2
        assert len(memory_chart['data']) == 2
    
    def test_dashboard_with_alerts(self):
        """Test dashboard integration with alerts."""
        dashboard = OptimizationDashboard()
        alert_system = AlertSystem()
        
        # Create alerts
        alert1 = alert_system.create_alert('warning', 'High CPU usage', 'monitoring', 80.0, 85.0)
        alert2 = alert_system.create_alert('error', 'Memory limit exceeded', 'monitoring', 90.0, 95.0)
        
        assert alert1['level'] == 'warning'
        assert alert2['level'] == 'error'
        
        # Get alerts
        alerts = alert_system.get_alerts()
        assert len(alerts) == 2
        
        # Get summary
        summary = alert_system.get_alert_summary()
        assert summary['total_alerts'] == 2
        assert summary['warning_count'] == 1
        assert summary['error_count'] == 1
    
    def test_dashboard_with_configuration(self):
        """Test dashboard integration with configuration."""
        dashboard = OptimizationDashboard()
        config_manager = ConfigurationManager()
        
        # Create configuration
        config = {
            'dashboard': {'port': 8080, 'host': 'localhost'},
            'monitoring': {'interval': 1, 'enabled': True},
            'charts': {'auto_update': True, 'max_points': 1000}
        }
        
        # Save configuration
        with patch('builtins.open', create=True) as mock_open:
            mock_open.return_value.__enter__.return_value.write.return_value = None
            
            save_result = config_manager.save_configuration(config)
            assert save_result['saved'] is True
        
        # Validate configuration
        validation_result = config_manager.validate_configuration(config)
        assert validation_result['valid'] is True


if __name__ == '__main__':
    pytest.main([__file__])
