import 'dart:async'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../../../core/network/api_client.dart'; import '../../../core/network/signalr_service.dart'; import '../../../core/theme/app_theme.dart'; import '../../../core/widgets/glass_container.dart'; import '../../../core/widgets/status_badge.dart'; import '../views/service_detail_screen.dart'; /// Real-Time System Diagnostics Widget driven EXCLUSIVELY over SignalR WebSockets (`/hubs/health`). /// ZERO REST HTTP API calls are performed. class SystemDiagnosticsWidget extends StatefulWidget { final SignalRService? signalRService; const SystemDiagnosticsWidget({ super.key, this.signalRService, }); @override State createState() => _SystemDiagnosticsWidgetState(); } class _SystemDiagnosticsWidgetState extends State { List> _serviceStatuses = []; StreamSubscription>>? _healthSub; @override void initState() { super.initState(); if (widget.signalRService != null) { _healthSub = widget.signalRService!.healthStream.listen((data) { if (mounted) { setState(() { _serviceStatuses = data; }); } }); } } @override void dispose() { _healthSub?.cancel(); super.dispose(); } @override Widget build(BuildContext context) { final int totalCount = _serviceStatuses.length; final int onlineCount = _serviceStatuses.where((s) => s['status']?.toString().toLowerCase() == 'online').length; final bool isWsConnected = widget.signalRService?.isConnected ?? false; return SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // WebSocket Status Banner GlassContainer( padding: const EdgeInsets.all(16), child: Row( children: [ Container( padding: const EdgeInsets.all(10), decoration: BoxDecoration( color: AppTheme.primaryEmerald.withValues(alpha: 0.15), shape: BoxShape.circle, border: Border.all(color: AppTheme.primaryEmerald.withValues(alpha: 0.3)), ), child: Icon(Icons.sensors_rounded, color: AppTheme.primaryEmerald, size: 22), ), const SizedBox(width: 14), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ const Text( 'SignalR WebSocket Live-Diagnose', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 15), ), const SizedBox(width: 8), Container( width: 8, height: 8, decoration: BoxDecoration( color: isWsConnected ? AppTheme.primaryEmerald : AppTheme.accentRed, shape: BoxShape.circle, boxShadow: [ BoxShadow( color: (isWsConnected ? AppTheme.primaryEmerald : AppTheme.accentRed).withValues(alpha: 0.8), blurRadius: 6, spreadRadius: 2, ), ], ), ), ], ), const SizedBox(height: 2), Text( '100% über SignalR WebSockets (/hubs/health). Keine HTTP API Anfragen.', style: TextStyle(fontSize: 12, color: AppTheme.textSecondary), ), ], ), ), const SizedBox(width: 10), Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), decoration: BoxDecoration( color: (onlineCount == totalCount && totalCount > 0 ? AppTheme.primaryEmerald : AppTheme.accentCyan).withValues(alpha: 0.2), borderRadius: BorderRadius.circular(10), border: Border.all(color: (onlineCount == totalCount && totalCount > 0 ? AppTheme.primaryEmerald : AppTheme.accentCyan).withValues(alpha: 0.4)), ), child: Text( totalCount > 0 ? '$onlineCount / $totalCount Online' : 'Verbinde WebSocket...', style: TextStyle( color: onlineCount == totalCount && totalCount > 0 ? AppTheme.primaryEmerald : AppTheme.accentCyan, fontWeight: FontWeight.bold, fontSize: 13, ), ), ), ], ), ), const SizedBox(height: 16), const Text( 'Echtzeit Dienststatus (SignalR Push)', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16), ), const SizedBox(height: 12), if (_serviceStatuses.isEmpty) Padding( padding: const EdgeInsets.symmetric(vertical: 40), child: Center( child: Column( children: [ CircularProgressIndicator(color: AppTheme.primaryEmerald), const SizedBox(height: 16), Text('Warte auf SignalR WebSocket Daten von /hubs/health...', style: TextStyle(color: AppTheme.textMuted, fontSize: 13)), ], ), ), ) else GridView.builder( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), itemCount: _serviceStatuses.length, gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: MediaQuery.of(context).size.width > 900 ? 2 : 1, childAspectRatio: 2.7, crossAxisSpacing: 12, mainAxisSpacing: 12, ), itemBuilder: (context, index) { final svc = _serviceStatuses[index]; final String name = svc['name']?.toString() ?? 'Unbekannt'; final String type = svc['type']?.toString() ?? ''; final String status = svc['status']?.toString() ?? 'Offline'; final bool isOnline = status.toLowerCase() == 'online'; final String portInfo = svc['port']?.toString() ?? 'MQTT Only'; final String db = svc['db']?.toString() ?? 'PostgreSQL'; return InkWell( onTap: () { final apiClient = context.read(); Navigator.push( context, MaterialPageRoute( builder: (_) => ServiceDetailScreen(serviceName: name, apiClient: apiClient), ), ); }, borderRadius: BorderRadius.circular(20), child: GlassContainer( padding: const EdgeInsets.all(14), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( children: [ Icon( name == 'FinlyticBackend' ? Icons.hub_outlined : Icons.dns_outlined, size: 18, color: isOnline ? AppTheme.primaryEmerald : AppTheme.accentRed, ), const SizedBox(width: 8), Text( name, style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 14), ), ], ), StatusBadge( label: status, color: isOnline ? AppTheme.primaryEmerald : AppTheme.accentRed, ), ], ), Text( type, style: TextStyle(fontSize: 12, color: AppTheme.textSecondary), overflow: TextOverflow.ellipsis, ), const Divider(height: 10, color: Colors.white10), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( children: [ Icon(Icons.storage_outlined, size: 12, color: AppTheme.textMuted), const SizedBox(width: 4), Text( db, style: TextStyle(fontSize: 11, color: AppTheme.textMuted), ), ], ), Row( children: [ Icon(name == 'FinlyticBackend' ? Icons.language_outlined : Icons.cable_outlined, size: 12, color: AppTheme.accentCyan), const SizedBox(width: 4), Text( portInfo, style: TextStyle( fontSize: 11, color: name == 'FinlyticBackend' ? AppTheme.primaryEmerald : AppTheme.accentCyan, fontWeight: FontWeight.w600, ), ), ], ), ], ), ], ), ), ); }, ), ], ), ); } }