"""
Echtzeit-Dashboard-System für das MYP-System
==========================================

Dieses Modul stellt ein erweiterte Dashboard mit Echtzeit-Updates bereit:
- WebSocket-basierte Live-Updates
- Ereignis-gesteuerte Benachrichtigungen
- Modulare Dashboard-Widgets
- Performance-optimierte Datenabfragen
- Responsive Design mit automatischer Anpassung
"""

import asyncio
import json
import logging
import time
from datetime import datetime, timedelta
from typing import Dict, List, Any, Optional, Callable, Set
from dataclasses import dataclass, asdict
from enum import Enum
from collections import defaultdict, deque
import threading
from concurrent.futures import ThreadPoolExecutor

# WebSocket-Support
try:
    from flask_socketio import SocketIO, emit, join_room, leave_room, disconnect
    # Eventlet separat importieren für bessere Fehlerbehandlung
    try:
        from eventlet import wsgi, listen
        import eventlet
        EVENTLET_AVAILABLE = True
    except (ImportError, AttributeError) as e:
        # Eventlet nicht verfügbar oder nicht kompatibel (z.B. Python 3.13)
        print(f"⚠️ Eventlet nicht verfügbar: {e}")
        print("🔄 Fallback auf standard threading mode für WebSocket")
        EVENTLET_AVAILABLE = False
        eventlet = None
        wsgi = None
        listen = None
    
    WEBSOCKET_AVAILABLE = True
except ImportError as e:
    print(f"⚠️ Flask-SocketIO nicht verfügbar: {e}")
    print("📡 Dashboard läuft ohne Real-time Updates")
    WEBSOCKET_AVAILABLE = False
    EVENTLET_AVAILABLE = False
    SocketIO = None
    eventlet = None

from flask import request, session, current_app
from flask_login import current_user

from utils.logging_config import get_logger
from models import Job, User, Printer, Stats, GuestRequest, get_db_session
from utils.analytics import track_event, get_dashboard_stats

logger = get_logger("dashboard")

class EventType(Enum):
    """Typen von Dashboard-Ereignissen"""
    JOB_CREATED = "job_created"
    JOB_STARTED = "job_started" 
    JOB_FINISHED = "job_finished"
    JOB_CANCELLED = "job_cancelled"
    JOB_PAUSED = "job_paused"
    JOB_RESUMED = "job_resumed"
    
    PRINTER_ONLINE = "printer_online"
    PRINTER_OFFLINE = "printer_offline"
    PRINTER_BUSY = "printer_busy"
    PRINTER_IDLE = "printer_idle"
    
    USER_LOGIN = "user_login"
    USER_LOGOUT = "user_logout"
    USER_CREATED = "user_created"
    
    GUEST_REQUEST = "guest_request"
    GUEST_APPROVED = "guest_approved"
    GUEST_REJECTED = "guest_rejected"
    
    SYSTEM_ALERT = "system_alert"
    SYSTEM_WARNING = "system_warning"
    SYSTEM_INFO = "system_info"

@dataclass
class DashboardEvent:
    """Dashboard-Ereignis mit Metadaten"""
    event_type: EventType
    data: Dict[str, Any]
    timestamp: datetime
    user_id: Optional[int] = None
    priority: str = "normal"  # low, normal, high, critical
    
    def to_dict(self) -> Dict[str, Any]:
        """Konvertiert das Ereignis zu einem Dictionary"""
        return {
            'event_type': self.event_type.value,
            'data': self.data,
            'timestamp': self.timestamp.isoformat(),
            'user_id': self.user_id,
            'priority': self.priority
        }

@dataclass
class WidgetConfig:
    """Konfiguration für Dashboard-Widgets"""
    widget_id: str
    title: str
    widget_type: str  # chart, table, metric, alert, custom
    refresh_interval: int = 30  # Sekunden
    data_source: str = ""
    chart_type: Optional[str] = None  # line, bar, pie, donut
    filters: Dict[str, Any] = None
    permissions: List[str] = None
    size: str = "medium"  # small, medium, large, full

class DashboardManager:
    """Zentraler Manager für Dashboard-Funktionalität"""
    
    def __init__(self):
        self.socketio: Optional[SocketIO] = None
        self.connected_clients: Set[str] = set()
        self.user_rooms: Dict[int, Set[str]] = defaultdict(set)
        self.event_queue: deque = deque(maxlen=1000)
        self.event_handlers: Dict[EventType, List[Callable]] = defaultdict(list)
        self.widgets: Dict[str, WidgetConfig] = {}
        self.cached_data: Dict[str, Any] = {}
        self.cache_timestamps: Dict[str, datetime] = {}
        self.executor = ThreadPoolExecutor(max_workers=4)
        
        self._setup_default_widgets()
        self._start_background_tasks()
    
    def init_socketio(self, app, cors_allowed_origins="*"):
        """Initialisiert SocketIO für das Dashboard"""
        if not WEBSOCKET_AVAILABLE:
            logger.warning("WebSocket-Funktionalität nicht verfügbar. Flask-SocketIO installieren.")
            return None
        
        # Bestimme den async_mode basierend auf verfügbaren Bibliotheken
        if EVENTLET_AVAILABLE:
            async_mode = 'eventlet'
            logger.info("Dashboard WebSocket-Server wird mit eventlet initialisiert")
        else:
            async_mode = 'threading'
            logger.info("Dashboard WebSocket-Server wird mit threading initialisiert (eventlet-Fallback)")
        
        self.socketio = SocketIO(
            app, 
            cors_allowed_origins=cors_allowed_origins,
            logger=False,
            engineio_logger=False,
            async_mode=async_mode
        )
        
        self._setup_socket_handlers()
        logger.info(f"Dashboard WebSocket-Server initialisiert (async_mode: {async_mode})")
        return self.socketio
    
    def _setup_socket_handlers(self):
        """Richtet WebSocket-Event-Handler ein"""
        if not self.socketio:
            return
        
        @self.socketio.on('connect')
        def handle_connect(auth):
            """Behandelt neue WebSocket-Verbindungen"""
            if not current_user.is_authenticated:
                logger.warning(f"Nicht authentifizierte WebSocket-Verbindung von {request.remote_addr}")
                disconnect()
                return False
            
            client_id = request.sid
            user_id = current_user.id
            
            self.connected_clients.add(client_id)
            self.user_rooms[user_id].add(client_id)
            
            # Benutzer zu seiner persönlichen Room hinzufügen
            join_room(f"user_{user_id}")
            
            # Admin-Benutzer zu Admin-Room hinzufügen
            if current_user.is_admin:
                join_room("admin")
            
            logger.info(f"Dashboard-Verbindung: User {user_id} ({client_id})")
            
            # Initiale Dashboard-Daten senden
            self._send_initial_data(client_id, user_id)
            
            return True
        
        @self.socketio.on('disconnect')
        def handle_disconnect():
            """Behandelt getrennte WebSocket-Verbindungen"""
            client_id = request.sid
            user_id = current_user.id if current_user.is_authenticated else None
            
            self.connected_clients.discard(client_id)
            
            if user_id:
                self.user_rooms[user_id].discard(client_id)
                if not self.user_rooms[user_id]:
                    del self.user_rooms[user_id]
            
            logger.info(f"Dashboard-Trennung: User {user_id} ({client_id})")
        
        @self.socketio.on('subscribe_widget')
        def handle_widget_subscription(data):
            """Behandelt Widget-Abonnements"""
            widget_id = data.get('widget_id')
            refresh_interval = data.get('refresh_interval', 30)
            
            if widget_id in self.widgets:
                join_room(f"widget_{widget_id}")
                # Sofortige Datenaktualisierung
                widget_data = self._get_widget_data(widget_id)
                emit('widget_update', {
                    'widget_id': widget_id,
                    'data': widget_data,
                    'timestamp': datetime.now().isoformat()
                })
        
        @self.socketio.on('unsubscribe_widget')
        def handle_widget_unsubscription(data):
            """Behandelt Widget-Abbestellungen"""
            widget_id = data.get('widget_id')
            leave_room(f"widget_{widget_id}")
        
        @self.socketio.on('request_data')
        def handle_data_request(data):
            """Behandelt manuelle Datenabfragen"""
            data_type = data.get('type')
            filters = data.get('filters', {})
            
            try:
                if data_type == 'jobs':
                    result = self._get_jobs_data(filters)
                elif data_type == 'printers':
                    result = self._get_printers_data(filters)
                elif data_type == 'users':
                    result = self._get_users_data(filters)
                elif data_type == 'stats':
                    result = self._get_stats_data(filters)
                else:
                    result = {'error': 'Unbekannter Datentyp'}
                
                emit('data_response', {
                    'type': data_type,
                    'data': result,
                    'timestamp': datetime.now().isoformat()
                })
                
            except Exception as e:
                logger.error(f"Fehler bei Datenabfrage {data_type}: {str(e)}")
                emit('data_response', {
                    'type': data_type,
                    'error': str(e),
                    'timestamp': datetime.now().isoformat()
                })
    
    def _send_initial_data(self, client_id: str, user_id: int):
        """Sendet initiale Dashboard-Daten an einen neuen Client"""
        try:
            # Basis-Dashboard-Daten
            dashboard_data = {
                'widgets': {wid: asdict(widget) for wid, widget in self.widgets.items()},
                'user_permissions': self._get_user_permissions(user_id),
                'system_status': self._get_system_status(),
                'recent_events': [event.to_dict() for event in list(self.event_queue)[-10:]]
            }
            
            self.socketio.emit('dashboard_init', dashboard_data, room=client_id)
            
        except Exception as e:
            logger.error(f"Fehler beim Senden der initialen Daten: {str(e)}")
    
    def _setup_default_widgets(self):
        """Richtet Standard-Dashboard-Widgets ein"""
        self.widgets = {
            'active_jobs': WidgetConfig(
                widget_id='active_jobs',
                title='Aktive Jobs',
                widget_type='metric',
                refresh_interval=15,
                data_source='jobs',
                size='small'
            ),
            'online_printers': WidgetConfig(
                widget_id='online_printers',
                title='Online Drucker',
                widget_type='metric',
                refresh_interval=30,
                data_source='printers',
                size='small'
            ),
            'jobs_timeline': WidgetConfig(
                widget_id='jobs_timeline',
                title='Jobs Timeline',
                widget_type='chart',
                chart_type='line',
                refresh_interval=60,
                data_source='jobs',
                size='large'
            ),
            'printer_status': WidgetConfig(
                widget_id='printer_status',
                title='Drucker Status',
                widget_type='chart',
                chart_type='donut',
                refresh_interval=30,
                data_source='printers',
                size='medium'
            ),
            'recent_jobs': WidgetConfig(
                widget_id='recent_jobs',
                title='Letzte Jobs',
                widget_type='table',
                refresh_interval=30,
                data_source='jobs',
                size='large'
            ),
            'system_alerts': WidgetConfig(
                widget_id='system_alerts',
                title='System-Meldungen',
                widget_type='alert',
                refresh_interval=10,
                data_source='alerts',
                size='full'
            ),
            'user_activity': WidgetConfig(
                widget_id='user_activity',
                title='Benutzer-Aktivität',
                widget_type='chart',
                chart_type='bar',
                refresh_interval=300,
                data_source='users',
                size='medium',
                permissions=['admin']
            ),
            'guest_requests': WidgetConfig(
                widget_id='guest_requests',
                title='Gast-Anfragen',
                widget_type='metric',
                refresh_interval=60,
                data_source='guest_requests',
                size='small',
                permissions=['admin']
            )
        }
    
    def _start_background_tasks(self):
        """Startet Hintergrundaufgaben für das Dashboard"""
        def background_worker():
            """Hintergrund-Worker für regelmäßige Updates"""
            while True:
                try:
                    self._update_cached_data()
                    self._broadcast_widget_updates()
                    self._cleanup_old_events()
                    time.sleep(10)  # Alle 10 Sekunden prüfen
                except Exception as e:
                    logger.error(f"Fehler im Dashboard-Background-Worker: {str(e)}")
                    time.sleep(30)  # Längere Pause bei Fehlern
        
        # Background-Thread starten
        background_thread = threading.Thread(target=background_worker, daemon=True)
        background_thread.start()
        logger.info("Dashboard-Background-Worker gestartet")
    
    def _update_cached_data(self):
        """Aktualisiert gecachte Dashboard-Daten"""
        current_time = datetime.now()
        
        for widget_id, widget in self.widgets.items():
            cache_key = f"widget_{widget_id}"
            last_update = self.cache_timestamps.get(cache_key, datetime.min)
            
            if (current_time - last_update).total_seconds() >= widget.refresh_interval:
                try:
                    new_data = self._get_widget_data(widget_id)
                    self.cached_data[cache_key] = new_data
                    self.cache_timestamps[cache_key] = current_time
                except Exception as e:
                    logger.error(f"Fehler beim Cache-Update für Widget {widget_id}: {str(e)}")
    
    def _broadcast_widget_updates(self):
        """Sendet Widget-Updates an alle verbundenen Clients"""
        if not self.socketio:
            return
        
        current_time = datetime.now()
        
        for widget_id, widget in self.widgets.items():
            cache_key = f"widget_{widget_id}"
            
            if cache_key in self.cached_data:
                widget_data = self.cached_data[cache_key]
                
                self.socketio.emit('widget_update', {
                    'widget_id': widget_id,
                    'data': widget_data,
                    'timestamp': current_time.isoformat()
                }, room=f"widget_{widget_id}")
    
    def _get_widget_data(self, widget_id: str) -> Dict[str, Any]:
        """Holt Daten für ein spezifisches Widget"""
        widget = self.widgets.get(widget_id)
        if not widget:
            return {'error': 'Widget nicht gefunden'}
        
        try:
            if widget.data_source == 'jobs':
                return self._get_jobs_widget_data(widget)
            elif widget.data_source == 'printers':
                return self._get_printers_widget_data(widget)
            elif widget.data_source == 'users':
                return self._get_users_widget_data(widget)
            elif widget.data_source == 'alerts':
                return self._get_alerts_widget_data(widget)
            elif widget.data_source == 'guest_requests':
                return self._get_guest_requests_widget_data(widget)
            else:
                return {'error': 'Unbekannte Datenquelle'}
                
        except Exception as e:
            logger.error(f"Fehler beim Laden der Widget-Daten für {widget_id}: {str(e)}")
            return {'error': str(e)}
    
    def _get_jobs_widget_data(self, widget: WidgetConfig) -> Dict[str, Any]:
        """Holt Job-Daten für Widgets"""
        with get_db_session() as db_session:
            if widget.widget_type == 'metric' and widget.widget_id == 'active_jobs':
                # Anzahl aktiver Jobs
                count = db_session.query(Job).filter(
                    Job.status.in_(['running', 'scheduled', 'paused'])
                ).count()
                return {
                    'value': count,
                    'label': 'Aktive Jobs',
                    'trend': self._calculate_trend('active_jobs', count)
                }
            
            elif widget.widget_type == 'chart' and widget.widget_id == 'jobs_timeline':
                # Jobs-Timeline für die letzten 24 Stunden
                since = datetime.now() - timedelta(hours=24)
                jobs = db_session.query(Job).filter(Job.created_at >= since).all()
                
                # Gruppiere nach Stunden
                hourly_data = defaultdict(int)
                for job in jobs:
                    hour = job.created_at.replace(minute=0, second=0, microsecond=0)
                    hourly_data[hour] += 1
                
                # Sortierte Timeline erstellen
                timeline_data = []
                for i in range(24):
                    hour = (datetime.now() - timedelta(hours=23-i)).replace(minute=0, second=0, microsecond=0)
                    timeline_data.append({
                        'time': hour.isoformat(),
                        'value': hourly_data.get(hour, 0)
                    })
                
                return {
                    'chart_type': 'line',
                    'data': timeline_data,
                    'title': 'Jobs der letzten 24 Stunden'
                }
            
            elif widget.widget_type == 'table' and widget.widget_id == 'recent_jobs':
                # Letzte 10 Jobs
                jobs = db_session.query(Job).order_by(Job.created_at.desc()).limit(10).all()
                
                job_data = []
                for job in jobs:
                    job_data.append({
                        'id': job.id,
                        'name': job.name,
                        'user': job.user.name if job.user else 'Unbekannt',
                        'printer': job.printer.name if job.printer else 'Unbekannt',
                        'status': job.status,
                        'created': job.created_at.strftime('%d.%m.%Y %H:%M') if job.created_at else ''
                    })
                
                return {
                    'headers': ['ID', 'Name', 'Benutzer', 'Drucker', 'Status', 'Erstellt'],
                    'rows': job_data
                }
        
        return {}
    
    def _get_printers_widget_data(self, widget: WidgetConfig) -> Dict[str, Any]:
        """Holt Drucker-Daten für Widgets"""
        with get_db_session() as db_session:
            if widget.widget_type == 'metric' and widget.widget_id == 'online_printers':
                # Anzahl online Drucker
                count = db_session.query(Printer).filter(
                    Printer.status.in_(['online', 'idle', 'available'])
                ).count()
                total = db_session.query(Printer).filter(Printer.active == True).count()
                
                return {
                    'value': count,
                    'total': total,
                    'label': f'{count}/{total} Online',
                    'percentage': (count / total * 100) if total > 0 else 0
                }
            
            elif widget.widget_type == 'chart' and widget.widget_id == 'printer_status':
                # Drucker-Status-Verteilung
                printers = db_session.query(Printer).filter(Printer.active == True).all()
                
                status_counts = defaultdict(int)
                for printer in printers:
                    status_counts[printer.status] += 1
                
                chart_data = []
                colors = {
                    'online': '#10b981',
                    'idle': '#3b82f6', 
                    'busy': '#f59e0b',
                    'offline': '#ef4444',
                    'maintenance': '#8b5cf6'
                }
                
                for status, count in status_counts.items():
                    chart_data.append({
                        'label': status.title(),
                        'value': count,
                        'color': colors.get(status, '#6b7280')
                    })
                
                return {
                    'chart_type': 'donut',
                    'data': chart_data,
                    'title': 'Drucker Status'
                }
        
        return {}
    
    def _get_users_widget_data(self, widget: WidgetConfig) -> Dict[str, Any]:
        """Holt Benutzer-Daten für Widgets (nur für Admins)"""
        with get_db_session() as db_session:
            if widget.widget_type == 'chart' and widget.widget_id == 'user_activity':
                # Benutzer-Aktivität der letzten 7 Tage
                since = datetime.now() - timedelta(days=7)
                users = db_session.query(User).filter(
                    User.last_activity >= since
                ).all()
                
                # Gruppiere nach Tagen
                daily_data = defaultdict(set)
                for user in users:
                    if user.last_activity:
                        day = user.last_activity.date()
                        daily_data[day].add(user.id)
                
                # Chart-Daten erstellen
                chart_data = []
                for i in range(7):
                    day = (datetime.now() - timedelta(days=6-i)).date()
                    chart_data.append({
                        'date': day.isoformat(),
                        'active_users': len(daily_data.get(day, set()))
                    })
                
                return {
                    'chart_type': 'bar',
                    'data': chart_data,
                    'title': 'Aktive Benutzer (7 Tage)'
                }
        
        return {}
    
    def _get_guest_requests_widget_data(self, widget: WidgetConfig) -> Dict[str, Any]:
        """Holt Gast-Anfragen-Daten für Widgets"""
        with get_db_session() as db_session:
            if widget.widget_type == 'metric' and widget.widget_id == 'guest_requests':
                # Anzahl offener Gast-Anfragen
                count = db_session.query(GuestRequest).filter(
                    GuestRequest.status == 'pending'
                ).count()
                
                return {
                    'value': count,
                    'label': 'Offene Anfragen',
                    'urgency': 'high' if count > 5 else 'normal'
                }
        
        return {}
    
    def _get_alerts_widget_data(self, widget: WidgetConfig) -> Dict[str, Any]:
        """Holt System-Meldungen für Widgets"""
        # Sammle aktuelle System-Alerts
        alerts = []
        
        # Prüfe auf kritische Zustände
        with get_db_session() as db_session:
            # Offline Drucker
            offline_printers = db_session.query(Printer).filter(
                Printer.status == 'offline',
                Printer.active == True
            ).count()
            
            if offline_printers > 0:
                alerts.append({
                    'type': 'warning',
                    'message': f'{offline_printers} Drucker offline',
                    'timestamp': datetime.now().isoformat(),
                    'action': 'printers'
                })
            
            # Lange wartende Jobs
            waiting_jobs = db_session.query(Job).filter(
                Job.status == 'scheduled',
                Job.created_at < datetime.now() - timedelta(hours=1)
            ).count()
            
            if waiting_jobs > 0:
                alerts.append({
                    'type': 'info',
                    'message': f'{waiting_jobs} Jobs warten seit über 1 Stunde',
                    'timestamp': datetime.now().isoformat(),
                    'action': 'jobs'
                })
            
            # Gast-Anfragen
            pending_requests = db_session.query(GuestRequest).filter(
                GuestRequest.status == 'pending'
            ).count()
            
            if pending_requests > 3:
                alerts.append({
                    'type': 'warning',
                    'message': f'{pending_requests} unbearbeitete Gast-Anfragen',
                    'timestamp': datetime.now().isoformat(),
                    'action': 'guest_requests'
                })
        
        return {
            'alerts': alerts[-10:],  # Nur die neuesten 10
            'count': len(alerts)
        }
    
    def _calculate_trend(self, metric_key: str, current_value: int) -> str:
        """Berechnet den Trend für eine Metrik"""
        # Vereinfachte Trend-Berechnung
        # In einer echten Implementierung würde man historische Daten verwenden
        cache_key = f"trend_{metric_key}"
        previous_value = self.cached_data.get(cache_key, current_value)
        
        if current_value > previous_value:
            trend = "up"
        elif current_value < previous_value:
            trend = "down"
        else:
            trend = "stable"
        
        self.cached_data[cache_key] = current_value
        return trend
    
    def _get_user_permissions(self, user_id: int) -> List[str]:
        """Holt Benutzer-Berechtigungen für Dashboard-Features"""
        with get_db_session() as db_session:
            user = db_session.query(User).filter(User.id == user_id).first()
            
            if not user:
                return []
            
            permissions = ['view_dashboard']
            
            if user.is_admin:
                permissions.extend([
                    'view_all_jobs',
                    'manage_users',
                    'manage_printers',
                    'view_system_stats',
                    'view_guest_requests'
                ])
            else:
                permissions.extend([
                    'view_own_jobs',
                    'create_jobs'
                ])
            
            return permissions
    
    def _get_system_status(self) -> Dict[str, Any]:
        """Holt aktuellen System-Status"""
        with get_db_session() as db_session:
            # Basis-Statistiken
            total_jobs = db_session.query(Job).count()
            active_jobs = db_session.query(Job).filter(
                Job.status.in_(['running', 'scheduled', 'paused'])
            ).count()
            total_printers = db_session.query(Printer).filter(Printer.active == True).count()
            online_printers = db_session.query(Printer).filter(
                Printer.status.in_(['online', 'idle', 'available'])
            ).count()
            total_users = db_session.query(User).filter(User.active == True).count()
            
            return {
                'jobs': {
                    'total': total_jobs,
                    'active': active_jobs
                },
                'printers': {
                    'total': total_printers,
                    'online': online_printers
                },
                'users': {
                    'total': total_users
                },
                'last_updated': datetime.now().isoformat()
            }
    
    def _cleanup_old_events(self):
        """Bereinigt alte Ereignisse aus der Queue"""
        cutoff_time = datetime.now() - timedelta(hours=24)
        
        # Entferne Ereignisse älter als 24 Stunden
        while self.event_queue and self.event_queue[0].timestamp < cutoff_time:
            self.event_queue.popleft()
    
    def emit_event(self, event: DashboardEvent):
        """Sendet ein Ereignis an alle verbundenen Clients"""
        if not self.socketio:
            return
        
        # Ereignis zur Queue hinzufügen
        self.event_queue.append(event)
        
        # Ereignis an relevante Clients senden
        room = None
        if event.user_id:
            room = f"user_{event.user_id}"
        elif event.priority in ['high', 'critical']:
            room = "admin"  # Kritische Ereignisse nur an Admins
        
        self.socketio.emit('dashboard_event', event.to_dict(), room=room)
        
        # Event-Handler ausführen
        for handler in self.event_handlers[event.event_type]:
            try:
                handler(event)
            except Exception as e:
                logger.error(f"Fehler im Event-Handler: {str(e)}")
    
    def register_event_handler(self, event_type: EventType, handler: Callable):
        """Registriert einen Event-Handler"""
        self.event_handlers[event_type].append(handler)
    
    def add_custom_widget(self, widget: WidgetConfig):
        """Fügt ein benutzerdefiniertes Widget hinzu"""
        self.widgets[widget.widget_id] = widget
        logger.info(f"Custom Widget hinzugefügt: {widget.widget_id}")
    
    def get_dashboard_config(self, user_id: int) -> Dict[str, Any]:
        """Holt Dashboard-Konfiguration für einen Benutzer"""
        user_permissions = self._get_user_permissions(user_id)
        
        # Filtere Widgets basierend auf Berechtigungen
        available_widgets = {}
        for widget_id, widget in self.widgets.items():
            if not widget.permissions or any(perm in user_permissions for perm in widget.permissions):
                available_widgets[widget_id] = asdict(widget)
        
        return {
            'widgets': available_widgets,
            'permissions': user_permissions,
            'refresh_intervals': {
                wid: widget.refresh_interval 
                for wid, widget in available_widgets.items()
            }
        }

# Globale Dashboard-Manager-Instanz
dashboard_manager = DashboardManager()

# Convenience-Funktionen für Events
def emit_job_event(event_type: EventType, job: Job, user_id: int = None):
    """Sendet ein Job-Ereignis"""
    event = DashboardEvent(
        event_type=event_type,
        data={
            'job_id': job.id,
            'job_name': job.name,
            'status': job.status,
            'user_name': job.user.name if job.user else 'Unbekannt',
            'printer_name': job.printer.name if job.printer else 'Unbekannt'
        },
        timestamp=datetime.now(),
        user_id=user_id,
        priority='normal'
    )
    dashboard_manager.emit_event(event)

def emit_printer_event(event_type: EventType, printer: Printer):
    """Sendet ein Drucker-Ereignis"""
    event = DashboardEvent(
        event_type=event_type,
        data={
            'printer_id': printer.id,
            'printer_name': printer.name,
            'status': printer.status,
            'location': printer.location or 'Unbekannt'
        },
        timestamp=datetime.now(),
        priority='normal' if event_type in [EventType.PRINTER_ONLINE, EventType.PRINTER_IDLE] else 'high'
    )
    dashboard_manager.emit_event(event)

def emit_system_alert(message: str, alert_type: str = "info", priority: str = "normal"):
    """Sendet eine System-Meldung"""
    event_type_map = {
        'info': EventType.SYSTEM_INFO,
        'warning': EventType.SYSTEM_WARNING,
        'alert': EventType.SYSTEM_ALERT
    }
    
    event = DashboardEvent(
        event_type=event_type_map.get(alert_type, EventType.SYSTEM_INFO),
        data={
            'message': message,
            'alert_type': alert_type
        },
        timestamp=datetime.now(),
        priority=priority
    )
    dashboard_manager.emit_event(event)

# JavaScript für Frontend-Integration
def get_dashboard_client_js() -> str:
    """Generiert JavaScript für Dashboard-Client"""
    return """
    class DashboardClient {
        constructor(socketUrl = '') {
            this.socket = null;
            this.widgets = {};
            this.eventHandlers = {};
            this.reconnectAttempts = 0;
            this.maxReconnectAttempts = 5;
            this.reconnectDelay = 1000;
            
            this.connect(socketUrl);
        }
        
        connect(socketUrl) {
            if (typeof io === 'undefined') {
                console.error('Socket.IO nicht verfügbar');
                return;
            }
            
            this.socket = io(socketUrl);
            
            this.socket.on('connect', () => {
                console.log('Dashboard verbunden');
                this.reconnectAttempts = 0;
                this.onConnected();
            });
            
            this.socket.on('disconnect', () => {
                console.log('Dashboard getrennt');
                this.onDisconnected();
                this.attemptReconnect();
            });
            
            this.socket.on('dashboard_init', (data) => {
                this.handleDashboardInit(data);
            });
            
            this.socket.on('widget_update', (data) => {
                this.handleWidgetUpdate(data);
            });
            
            this.socket.on('dashboard_event', (event) => {
                this.handleDashboardEvent(event);
            });
            
            this.socket.on('data_response', (data) => {
                this.handleDataResponse(data);
            });
        }
        
        attemptReconnect() {
            if (this.reconnectAttempts < this.maxReconnectAttempts) {
                setTimeout(() => {
                    console.log(`Reconnect-Versuch ${this.reconnectAttempts + 1}`);
                    this.reconnectAttempts++;
                    this.socket.connect();
                }, this.reconnectDelay * Math.pow(2, this.reconnectAttempts));
            }
        }
        
        subscribeWidget(widgetId, refreshInterval = 30) {
            if (this.socket) {
                this.socket.emit('subscribe_widget', {
                    widget_id: widgetId,
                    refresh_interval: refreshInterval
                });
            }
        }
        
        unsubscribeWidget(widgetId) {
            if (this.socket) {
                this.socket.emit('unsubscribe_widget', {
                    widget_id: widgetId
                });
            }
        }
        
        requestData(type, filters = {}) {
            if (this.socket) {
                this.socket.emit('request_data', {
                    type: type,
                    filters: filters
                });
            }
        }
        
        handleDashboardInit(data) {
            this.widgets = data.widgets || {};
            
            // Ereignis-Handler aufrufen
            this.fireEvent('dashboard_init', data);
            
            // Auto-Subscribe für alle Widgets
            Object.keys(this.widgets).forEach(widgetId => {
                this.subscribeWidget(widgetId, this.widgets[widgetId].refresh_interval);
            });
        }
        
        handleWidgetUpdate(data) {
            const widgetId = data.widget_id;
            const widgetData = data.data;
            
            // Widget-Element suchen und aktualisieren
            const widgetElement = document.querySelector(`[data-widget-id="${widgetId}"]`);
            if (widgetElement) {
                this.updateWidgetElement(widgetElement, widgetData);
            }
            
            // Ereignis-Handler aufrufen
            this.fireEvent('widget_update', data);
        }
        
        handleDashboardEvent(event) {
            // Event-Notification anzeigen
            this.showNotification(event);
            
            // Ereignis-Handler aufrufen
            this.fireEvent('dashboard_event', event);
        }
        
        handleDataResponse(data) {
            // Ereignis-Handler aufrufen
            this.fireEvent('data_response', data);
        }
        
        updateWidgetElement(element, data) {
            const widgetType = element.dataset.widgetType;
            
            switch (widgetType) {
                case 'metric':
                    this.updateMetricWidget(element, data);
                    break;
                case 'chart':
                    this.updateChartWidget(element, data);
                    break;
                case 'table':
                    this.updateTableWidget(element, data);
                    break;
                case 'alert':
                    this.updateAlertWidget(element, data);
                    break;
            }
        }
        
        updateMetricWidget(element, data) {
            const valueElement = element.querySelector('.metric-value');
            const labelElement = element.querySelector('.metric-label');
            const trendElement = element.querySelector('.metric-trend');
            
            if (valueElement && data.value !== undefined) {
                valueElement.textContent = data.value;
            }
            
            if (labelElement && data.label) {
                labelElement.textContent = data.label;
            }
            
            if (trendElement && data.trend) {
                trendElement.className = `metric-trend trend-${data.trend}`;
                trendElement.innerHTML = this.getTrendIcon(data.trend);
            }
        }
        
        updateChartWidget(element, data) {
            const chartContainer = element.querySelector('.chart-container');
            if (chartContainer && data.data) {
                // Chart.js oder andere Chart-Library verwenden
                this.renderChart(chartContainer, data);
            }
        }
        
        updateTableWidget(element, data) {
            const tableBody = element.querySelector('tbody');
            if (tableBody && data.rows) {
                tableBody.innerHTML = '';
                
                data.rows.forEach(row => {
                    const tr = document.createElement('tr');
                    Object.values(row).forEach(cellValue => {
                        const td = document.createElement('td');
                        td.textContent = cellValue;
                        tr.appendChild(td);
                    });
                    tableBody.appendChild(tr);
                });
            }
        }
        
        updateAlertWidget(element, data) {
            const alertContainer = element.querySelector('.alerts-container');
            if (alertContainer && data.alerts) {
                alertContainer.innerHTML = '';
                
                data.alerts.forEach(alert => {
                    const alertElement = this.createAlertElement(alert);
                    alertContainer.appendChild(alertElement);
                });
            }
        }
        
        createAlertElement(alert) {
            const div = document.createElement('div');
            div.className = `alert alert-${alert.type}`;
            div.innerHTML = `
                <div class="alert-content">
                    <span class="alert-message">${alert.message}</span>
                    <span class="alert-time">${new Date(alert.timestamp).toLocaleTimeString()}</span>
                </div>
            `;
            return div;
        }
        
        showNotification(event) {
            // Toast-Notification anzeigen
            const notification = document.createElement('div');
            notification.className = `notification notification-${event.priority}`;
            notification.innerHTML = `
                <div class="notification-content">
                    <strong>${event.event_type.replace('_', ' ').toUpperCase()}</strong>
                    <p>${this.formatEventMessage(event)}</p>
                </div>
                <button class="notification-close">&times;</button>
            `;
            
            // Auto-dismiss nach 5 Sekunden
            setTimeout(() => {
                notification.remove();
            }, 5000);
            
            // Close-Button
            notification.querySelector('.notification-close').onclick = () => {
                notification.remove();
            };
            
            // Notification-Container finden oder erstellen
            let container = document.querySelector('.notifications-container');
            if (!container) {
                container = document.createElement('div');
                container.className = 'notifications-container';
                document.body.appendChild(container);
            }
            
            container.appendChild(notification);
        }
        
        formatEventMessage(event) {
            const data = event.data;
            
            switch (event.event_type) {
                case 'job_created':
                    return `Neuer Job "${data.job_name}" von ${data.user_name}`;
                case 'job_started':
                    return `Job "${data.job_name}" gestartet auf ${data.printer_name}`;
                case 'job_finished':
                    return `Job "${data.job_name}" abgeschlossen`;
                case 'printer_offline':
                    return `Drucker "${data.printer_name}" ist offline`;
                case 'printer_online':
                    return `Drucker "${data.printer_name}" ist online`;
                default:
                    return data.message || 'Ereignis aufgetreten';
            }
        }
        
        getTrendIcon(trend) {
            switch (trend) {
                case 'up':
                    return '↗';
                case 'down':
                    return '↘';
                default:
                    return '→';
            }
        }
        
        renderChart(container, data) {
            // Placeholder für Chart-Rendering
            // Hier würde Chart.js oder eine andere Chart-Library verwendet
            container.innerHTML = `<p>Chart: ${data.title}</p>`;
        }
        
        on(event, handler) {
            if (!this.eventHandlers[event]) {
                this.eventHandlers[event] = [];
            }
            this.eventHandlers[event].push(handler);
        }
        
        fireEvent(event, data) {
            if (this.eventHandlers[event]) {
                this.eventHandlers[event].forEach(handler => {
                    try {
                        handler(data);
                    } catch (e) {
                        console.error('Fehler im Event-Handler:', e);
                    }
                });
            }
        }
        
        onConnected() {
            // Connection-Status anzeigen
            this.updateConnectionStatus('connected');
        }
        
        onDisconnected() {
            // Connection-Status anzeigen
            this.updateConnectionStatus('disconnected');
        }
        
        updateConnectionStatus(status) {
            const statusElement = document.querySelector('.connection-status');
            if (statusElement) {
                statusElement.className = `connection-status status-${status}`;
                statusElement.textContent = status === 'connected' ? 'Verbunden' : 'Getrennt';
            }
        }
    }
    
    // Auto-Initialize
    document.addEventListener('DOMContentLoaded', function() {
        if (typeof window.dashboardClient === 'undefined') {
            window.dashboardClient = new DashboardClient();
        }
    });
    """ 