"""Interactive optimization dashboard for performance monitoring and visualization."""

import time
import json
import threading
import uuid
from typing import Dict, List, Any, Optional, Callable
from dataclasses import dataclass, asdict
from collections import deque, defaultdict
import psutil
import numpy as np
from pathlib import Path
import tempfile
import os


@dataclass
class DashboardConfig:
    """Configuration for the optimization dashboard."""
    port: int = 8080
    host: str = 'localhost'
    auto_refresh_seconds: int = 5
    max_data_points: int = 1000
    enable_websockets: bool = True
    update_interval_seconds: int = 1
    max_history_size: int = 1000
    enable_alerts: bool = True
    metrics_enabled: List[str] = None
    chart_types: List[str] = None
    default_chart_type: str = 'line'
    auto_update: bool = True
    max_data_points_charts: int = 1000
    config_file: str = 'dashboard_config.json'
    auto_save: bool = True
    backup_enabled: bool = True
    max_backups: int = 5
    alert_levels: List[str] = None
    max_alerts: int = 100
    auto_clear_alerts: bool = True
    
    def __post_init__(self):
        if self.metrics_enabled is None:
            self.metrics_enabled = ['cpu', 'memory', 'throughput', 'latency']
        if self.chart_types is None:
            self.chart_types = ['line', 'bar', 'scatter', 'heatmap']
        if self.alert_levels is None:
            self.alert_levels = ['info', 'warning', 'error', 'critical']


class OptimizationDashboard:
    """Main dashboard orchestrating all monitoring and visualization components."""
    
    def __init__(self, **kwargs):
        """Initialize the optimization dashboard."""
        self.config = DashboardConfig(**kwargs)
        self.port = self.config.port
        self.host = self.config.host
        self.auto_refresh_seconds = self.config.auto_refresh_seconds
        self.max_data_points = self.config.max_data_points
        self.enable_websockets = self.config.enable_websockets
        
        # Initialize components
        self.monitor = RealTimeMonitor(
            update_interval_seconds=self.config.update_interval_seconds,
            max_history_size=self.config.max_history_size,
            enable_alerts=self.config.enable_alerts,
            metrics_enabled=self.config.metrics_enabled
        )
        self.charts = InteractiveCharts(
            chart_types=self.config.chart_types,
            default_chart_type=self.config.default_chart_type,
            auto_update=self.config.auto_update,
            max_data_points=self.config.max_data_points_charts
        )
        self.config_manager = ConfigurationManager(
            config_file=self.config.config_file,
            auto_save=self.config.auto_save,
            backup_enabled=self.config.backup_enabled,
            max_backups=self.config.max_backups
        )
        self.alert_system = AlertSystem(
            enable_alerts=self.config.enable_alerts,
            alert_levels=self.config.alert_levels,
            max_alerts=self.config.max_alerts,
            auto_clear=self.config.auto_clear_alerts
        )
        
        # Dashboard state
        self.is_running = False
        self.start_time = None
        self.metrics_data = deque(maxlen=self.max_data_points)
        self.active_connections = 0
        self.server_thread = None
    
    def start_dashboard(self) -> Dict[str, Any]:
        """Start the dashboard web server."""
        if self.is_running:
            return {'status': 'already_running', 'port': self.port}
        
        self.is_running = True
        self.start_time = time.time()
        
        # Start monitoring
        self.monitor.start_monitoring()
        
        # Mock web server startup
        self.server_thread = threading.Thread(target=self._run_server, daemon=True)
        self.server_thread.start()
        
        return {
            'status': 'started',
            'port': self.port,
            'url': f'http://{self.host}:{self.port}',
            'start_time': self.start_time
        }
    
    def stop_dashboard(self) -> Dict[str, Any]:
        """Stop the dashboard web server."""
        if not self.is_running:
            return {'status': 'not_running'}
        
        self.is_running = False
        shutdown_time = time.time()
        
        # Stop monitoring
        self.monitor.stop_monitoring()
        
        return {
            'status': 'stopped',
            'shutdown_time': shutdown_time,
            'uptime_seconds': shutdown_time - self.start_time if self.start_time else 0
        }
    
    def get_dashboard_status(self) -> Dict[str, Any]:
        """Get current dashboard status."""
        uptime = time.time() - self.start_time if self.start_time else 0
        
        return {
            'status': 'running' if self.is_running else 'stopped',
            'uptime_seconds': uptime,
            'active_connections': self.active_connections,
            'data_points_count': len(self.metrics_data),
            'monitoring_status': self.monitor.get_status(),
            'alert_count': len(self.alert_system.alerts)
        }
    
    def update_metrics(self, metrics: Dict[str, Any]) -> Dict[str, Any]:
        """Update dashboard metrics."""
        timestamp = time.time()
        metrics_with_timestamp = {**metrics, 'timestamp': timestamp}
        
        self.metrics_data.append(metrics_with_timestamp)
        
        # Check for alerts
        if self.alert_system.enable_alerts:
            self._check_metrics_alerts(metrics_with_timestamp)
        
        return {
            'updated': True,
            'timestamp': timestamp,
            'metrics_count': len(metrics),
            'total_data_points': len(self.metrics_data)
        }
    
    def get_dashboard_data(self) -> Dict[str, Any]:
        """Get comprehensive dashboard data."""
        return {
            'status': self.get_dashboard_status(),
            'metrics': list(self.metrics_data),
            'charts': self._generate_charts(),
            'alerts': self.alert_system.get_alerts(),
            'configuration': self.config_manager.load_configuration()
        }
    
    def _run_server(self):
        """Mock web server implementation."""
        # In a real implementation, this would start a web server
        # For now, we just simulate the server running
        while self.is_running:
            time.sleep(1)
    
    def _check_metrics_alerts(self, metrics: Dict[str, Any]):
        """Check metrics against thresholds and create alerts."""
        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 = self.alert_system.check_thresholds(metrics, thresholds)
        for alert in alerts:
            self.alert_system.alerts.append(alert)
    
    def _generate_charts(self) -> Dict[str, Any]:
        """Generate chart configurations."""
        data = list(self.metrics_data)
        
        return {
            'performance': self.charts.create_performance_chart(data, 'cpu_usage'),
            'memory': self.charts.create_memory_chart(data),
            'throughput': self.charts.create_throughput_chart(data),
            'combined': self.charts.create_combined_chart(data)
        }


class RealTimeMonitor:
    """Real-time system monitoring component."""
    
    def __init__(
        self,
        update_interval_seconds: int = 1,
        max_history_size: int = 1000,
        enable_alerts: bool = True,
        metrics_enabled: List[str] = None
    ):
        """Initialize the real-time monitor."""
        self.update_interval_seconds = update_interval_seconds
        self.max_history_size = max_history_size
        self.enable_alerts = enable_alerts
        self.metrics_enabled = metrics_enabled or ['cpu', 'memory', 'throughput', 'latency']
        
        self.is_monitoring = False
        self.metrics_history = deque(maxlen=max_history_size)
        self.monitor_thread = None
        self.start_time = None
    
    def start_monitoring(self) -> Dict[str, Any]:
        """Start real-time monitoring."""
        if self.is_monitoring:
            return {'status': 'already_monitoring'}
        
        self.is_monitoring = True
        self.start_time = time.time()
        
        # Start monitoring thread
        self.monitor_thread = threading.Thread(target=self._monitor_loop, daemon=True)
        self.monitor_thread.start()
        
        return {
            'status': 'started',
            'update_interval': self.update_interval_seconds,
            'metrics_enabled': self.metrics_enabled,
            'start_time': self.start_time
        }
    
    def stop_monitoring(self) -> Dict[str, Any]:
        """Stop real-time monitoring."""
        if not self.is_monitoring:
            return {'status': 'not_monitoring'}
        
        self.is_monitoring = False
        stop_time = time.time()
        
        return {
            'status': 'stopped',
            'stop_time': stop_time,
            'monitoring_duration': stop_time - self.start_time if self.start_time else 0
        }
    
    def collect_metrics(self) -> Dict[str, Any]:
        """Collect current system metrics."""
        metrics = {'timestamp': time.time()}
        
        if 'cpu' in self.metrics_enabled:
            metrics['cpu_usage'] = psutil.cpu_percent(interval=0.1)
        
        if 'memory' in self.metrics_enabled:
            memory = psutil.virtual_memory()
            metrics['memory_usage'] = memory.percent
        
        if 'throughput' in self.metrics_enabled:
            # Simulate throughput measurement
            metrics['throughput'] = np.random.uniform(10, 30)
        
        if 'latency' in self.metrics_enabled:
            # Simulate latency measurement
            metrics['latency'] = np.random.uniform(0.1, 0.5)
        
        return metrics
    
    def get_metrics_history(self) -> List[Dict[str, Any]]:
        """Get metrics history."""
        return list(self.metrics_history)
    
    def get_metrics_summary(self) -> Dict[str, Any]:
        """Get metrics summary statistics."""
        if not self.metrics_history:
            return {}
        
        summary = {}
        metrics_list = list(self.metrics_history)
        
        for metric in self.metrics_enabled:
            metric_key = f'{metric}_usage' if metric in ['cpu', 'memory'] else metric
            values = [m.get(metric_key, 0) for m in metrics_list if metric_key in m]
            
            if values:
                summary[metric_key] = {
                    f'avg_{metric_key}': np.mean(values),
                    f'max_{metric_key}': np.max(values),
                    f'min_{metric_key}': np.min(values),
                    f'std_{metric_key}': np.std(values)
                }
        
        return summary
    
    def get_status(self) -> Dict[str, Any]:
        """Get monitoring status."""
        return {
            'is_monitoring': self.is_monitoring,
            'update_interval': self.update_interval_seconds,
            'metrics_enabled': self.metrics_enabled,
            'history_size': len(self.metrics_history),
            'start_time': self.start_time
        }
    
    def _monitor_loop(self):
        """Main monitoring loop."""
        while self.is_monitoring:
            try:
                metrics = self.collect_metrics()
                self.metrics_history.append(metrics)
                time.sleep(self.update_interval_seconds)
            except Exception as e:
                # Log error and continue monitoring
                print(f"Monitoring error: {e}")
                time.sleep(self.update_interval_seconds)


class InteractiveCharts:
    """Interactive chart generation component."""
    
    def __init__(
        self,
        chart_types: List[str] = None,
        default_chart_type: str = 'line',
        auto_update: bool = True,
        max_data_points: int = 1000
    ):
        """Initialize the interactive charts component."""
        self.chart_types = chart_types or ['line', 'bar', 'scatter', 'heatmap']
        self.default_chart_type = default_chart_type
        self.auto_update = auto_update
        self.max_data_points = max_data_points
    
    def create_performance_chart(self, data: List[Dict[str, Any]], metric: str) -> Dict[str, Any]:
        """Create a performance chart for a specific metric."""
        chart_data = []
        
        for point in data:
            if metric in point and 'timestamp' in point:
                chart_data.append({
                    'x': point['timestamp'],
                    'y': point[metric]
                })
        
        return {
            'chart_type': self.default_chart_type,
            'data': chart_data,
            'options': {
                'title': f'{metric.replace("_", " ").title()} Over Time',
                'x_axis': 'Time',
                'y_axis': metric.replace('_', ' ').title(),
                'auto_update': self.auto_update
            }
        }
    
    def create_throughput_chart(self, data: List[Dict[str, Any]]) -> Dict[str, Any]:
        """Create a throughput chart."""
        chart_data = []
        
        for point in data:
            if 'throughput' in point and 'timestamp' in point:
                chart_data.append({
                    'x': point['timestamp'],
                    'y': point['throughput']
                })
        
        return {
            'chart_type': self.default_chart_type,
            'data': chart_data,
            'options': {
                'title': 'Throughput Over Time',
                'x_axis': 'Time',
                'y_axis': 'Throughput (files/min)',
                'auto_update': self.auto_update
            }
        }
    
    def create_memory_chart(self, data: List[Dict[str, Any]]) -> Dict[str, Any]:
        """Create a memory usage chart."""
        chart_data = []
        
        for point in data:
            if 'memory_usage' in point and 'timestamp' in point:
                chart_data.append({
                    'x': point['timestamp'],
                    'y': point['memory_usage']
                })
        
        return {
            'chart_type': self.default_chart_type,
            'data': chart_data,
            'options': {
                'title': 'Memory Usage Over Time',
                'x_axis': 'Time',
                'y_axis': 'Memory Usage (%)',
                'auto_update': self.auto_update
            }
        }
    
    def create_combined_chart(self, data: List[Dict[str, Any]]) -> Dict[str, Any]:
        """Create a combined chart with multiple metrics."""
        datasets = {}
        
        for point in data:
            if 'timestamp' in point:
                timestamp = point['timestamp']
                for key, value in point.items():
                    if key != 'timestamp' and isinstance(value, (int, float)):
                        if key not in datasets:
                            datasets[key] = []
                        datasets[key].append({'x': timestamp, 'y': value})
        
        return {
            'chart_type': self.default_chart_type,
            'data': datasets,
            'options': {
                'title': 'System Performance Overview',
                'x_axis': 'Time',
                'y_axis': 'Value',
                'auto_update': self.auto_update,
                'multiple_datasets': True
            }
        }
    
    def export_chart_data(self, data: List[Dict[str, Any]], format_type: str) -> Dict[str, Any]:
        """Export chart data in various formats."""
        if format_type == 'json':
            return {
                'format': 'json',
                'data': data,
                'timestamp': time.time()
            }
        elif format_type == 'csv':
            # Convert to CSV format
            csv_data = []
            if data:
                headers = list(data[0].keys())
                csv_data.append(','.join(headers))
                for row in data:
                    csv_data.append(','.join(str(row.get(h, '')) for h in headers))
            
            return {
                'format': 'csv',
                'data': '\n'.join(csv_data),
                'timestamp': time.time()
            }
        else:
            return {
                'format': 'unknown',
                'error': f'Unsupported format: {format_type}'
            }


class ConfigurationManager:
    """Configuration management component."""
    
    def __init__(
        self,
        config_file: str = 'dashboard_config.json',
        auto_save: bool = True,
        backup_enabled: bool = True,
        max_backups: int = 5
    ):
        """Initialize the configuration manager."""
        self.config_file = config_file
        self.auto_save = auto_save
        self.backup_enabled = backup_enabled
        self.max_backups = max_backups
        self.backup_dir = Path('dashboard_backups')
        self.backup_dir.mkdir(exist_ok=True)
    
    def load_configuration(self) -> Dict[str, Any]:
        """Load configuration from file."""
        try:
            if Path(self.config_file).exists():
                with open(self.config_file, 'r') as f:
                    return json.load(f)
            else:
                return self._get_default_config()
        except Exception as e:
            print(f"Error loading configuration: {e}")
            return self._get_default_config()
    
    def save_configuration(self, config: Dict[str, Any]) -> Dict[str, Any]:
        """Save configuration to file."""
        try:
            # Create backup if enabled
            if self.backup_enabled and Path(self.config_file).exists():
                self.create_backup(config)
            
            with open(self.config_file, 'w') as f:
                json.dump(config, f, indent=2)
            
            return {
                'saved': True,
                'timestamp': time.time(),
                'file': self.config_file
            }
        except Exception as e:
            return {
                'saved': False,
                'error': str(e),
                'timestamp': time.time()
            }
    
    def update_configuration(self, current_config: Dict[str, Any], updates: Dict[str, Any]) -> Dict[str, Any]:
        """Update configuration with new values."""
        new_config = current_config.copy()
        
        for section, section_updates in updates.items():
            if section not in new_config:
                new_config[section] = {}
            new_config[section].update(section_updates)
        
        return new_config
    
    def validate_configuration(self, config: Dict[str, Any]) -> Dict[str, Any]:
        """Validate configuration values."""
        errors = []
        
        # Validate dashboard section
        if 'dashboard' in config:
            dashboard = config['dashboard']
            if 'port' in dashboard and (not isinstance(dashboard['port'], int) or dashboard['port'] < 1):
                errors.append("Dashboard port must be a positive integer")
        
        # Validate monitoring section
        if 'monitoring' in config:
            monitoring = config['monitoring']
            if 'interval' in monitoring and (not isinstance(monitoring['interval'], (int, float)) or monitoring['interval'] <= 0):
                errors.append("Monitoring interval must be a positive number")
        
        return {
            'valid': len(errors) == 0,
            'errors': errors if errors else None
        }
    
    def create_backup(self, config: Dict[str, Any]) -> Dict[str, Any]:
        """Create a configuration backup."""
        try:
            timestamp = int(time.time())
            backup_file = self.backup_dir / f'config_backup_{timestamp}.json'
            
            with open(backup_file, 'w') as f:
                json.dump(config, f, indent=2)
            
            # Clean up old backups
            self._cleanup_old_backups()
            
            return {
                'backup_created': True,
                'backup_file': str(backup_file),
                'timestamp': timestamp
            }
        except Exception as e:
            return {
                'backup_created': False,
                'error': str(e),
                'timestamp': time.time()
            }
    
    def _get_default_config(self) -> Dict[str, Any]:
        """Get default configuration."""
        return {
            'dashboard': {
                'port': 8080,
                'host': 'localhost',
                'auto_refresh_seconds': 5,
                'max_data_points': 1000
            },
            'monitoring': {
                'interval': 1,
                'enabled': True,
                'metrics_enabled': ['cpu', 'memory', 'throughput', 'latency']
            },
            'charts': {
                'auto_update': True,
                'max_points': 1000,
                'default_type': 'line'
            },
            'alerts': {
                'enabled': True,
                'max_alerts': 100,
                'auto_clear': True
            }
        }
    
    def _cleanup_old_backups(self):
        """Clean up old backup files."""
        backup_files = sorted(self.backup_dir.glob('config_backup_*.json'))
        if len(backup_files) > self.max_backups:
            for backup_file in backup_files[:-self.max_backups]:
                backup_file.unlink()


class AlertSystem:
    """Alert system component."""
    
    def __init__(
        self,
        enable_alerts: bool = True,
        alert_levels: List[str] = None,
        max_alerts: int = 100,
        auto_clear: bool = True
    ):
        """Initialize the alert system."""
        self.enable_alerts = enable_alerts
        self.alert_levels = alert_levels or ['info', 'warning', 'error', 'critical']
        self.max_alerts = max_alerts
        self.auto_clear = auto_clear
        self.alerts = deque(maxlen=max_alerts)
        self.alert_counter = 0
    
    def create_alert(
        self,
        level: str,
        message: str,
        source: str,
        threshold: float = None,
        current_value: float = None
    ) -> Dict[str, Any]:
        """Create a new alert."""
        if not self.enable_alerts:
            return {}
        
        if level not in self.alert_levels:
            level = 'info'
        
        self.alert_counter += 1
        alert = {
            'id': str(self.alert_counter),
            'level': level,
            'message': message,
            'source': source,
            'threshold': threshold,
            'current_value': current_value,
            'timestamp': time.time(),
            'acknowledged': False
        }
        
        self.alerts.append(alert)
        return alert
    
    def get_alerts(self, level: str = None) -> List[Dict[str, Any]]:
        """Get alerts, optionally filtered by level."""
        alerts = list(self.alerts)
        
        if level:
            alerts = [alert for alert in alerts if alert['level'] == level]
        
        return alerts
    
    def clear_alerts(self) -> Dict[str, Any]:
        """Clear all alerts."""
        count = len(self.alerts)
        self.alerts.clear()
        
        return {
            'cleared': True,
            'count': count,
            'timestamp': time.time()
        }
    
    def acknowledge_alert(self, alert_id: str) -> Dict[str, Any]:
        """Acknowledge a specific alert."""
        for alert in self.alerts:
            if alert['id'] == alert_id:
                alert['acknowledged'] = True
                return {
                    'acknowledged': True,
                    'alert_id': alert_id,
                    'timestamp': time.time()
                }
        
        return {
            'acknowledged': False,
            'error': f'Alert {alert_id} not found',
            'timestamp': time.time()
        }
    
    def check_thresholds(self, metrics: Dict[str, Any], thresholds: Dict[str, Any]) -> List[Dict[str, Any]]:
        """Check metrics against thresholds and generate alerts."""
        alerts = []
        
        for metric, threshold_config in thresholds.items():
            if metric in metrics:
                current_value = metrics[metric]
                
                for level, threshold in threshold_config.items():
                    if level in self.alert_levels:
                        if current_value > threshold:
                            alert = self.create_alert(
                                level=level,
                                message=f'{metric} threshold exceeded: {current_value} > {threshold}',
                                source='threshold_monitor',
                                threshold=threshold,
                                current_value=current_value
                            )
                            if alert:
                                alerts.append(alert)
        
        return alerts
    
    def get_alert_summary(self) -> Dict[str, Any]:
        """Get alert summary statistics."""
        summary = {
            'total_alerts': len(self.alerts),
            'acknowledged_count': sum(1 for alert in self.alerts if alert.get('acknowledged', False)),
            'unacknowledged_count': sum(1 for alert in self.alerts if not alert.get('acknowledged', False))
        }
        
        # Count by level
        for level in self.alert_levels:
            summary[f'{level}_count'] = sum(1 for alert in self.alerts if alert['level'] == level)
        
        return summary


class DashboardComponent:
    """Generic dashboard component."""
    
    def __init__(
        self,
        name: str,
        enabled: bool = True,
        auto_refresh: bool = True,
        refresh_interval: int = 5
    ):
        """Initialize a dashboard component."""
        self.name = name
        self.enabled = enabled
        self.auto_refresh = auto_refresh
        self.refresh_interval = refresh_interval
        self.last_update = None
        self.data = {}
    
    def update(self, data: Dict[str, Any]) -> Dict[str, Any]:
        """Update component data."""
        self.data = data
        self.last_update = time.time()
        
        return {
            'updated': True,
            'timestamp': self.last_update,
            'data': self.data
        }
    
    def get_status(self) -> Dict[str, Any]:
        """Get component status."""
        return {
            'name': self.name,
            'enabled': self.enabled,
            'auto_refresh': self.auto_refresh,
            'refresh_interval': self.refresh_interval,
            'last_update': self.last_update,
            'data_keys': list(self.data.keys()) if self.data else []
        }
    
    def enable(self) -> Dict[str, Any]:
        """Enable the component."""
        self.enabled = True
        return {'enabled': True, 'timestamp': time.time()}
    
    def disable(self) -> Dict[str, Any]:
        """Disable the component."""
        self.enabled = False
        return {'enabled': False, 'timestamp': time.time()}
    
    def set_refresh_interval(self, interval: int) -> Dict[str, Any]:
        """Set the refresh interval."""
        self.refresh_interval = max(1, interval)
        return {
            'refresh_interval': self.refresh_interval,
            'timestamp': time.time()
        }
