import 'dart:async'; import 'dart:math'; import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import '../../app/theme.dart'; import '../../providers/score_provider.dart'; import '../../providers/session_provider.dart'; import '../../shared/api_client.dart'; import '../../shared/models/match.dart'; import '../../shared/models/score_state.dart'; import '../../shared/models/stream_session.dart'; import '../../shared/websocket_service.dart'; import '../../shared/regia_share.dart'; import '../../shared/watch_share.dart'; import '../../shared/widgets/metric_card.dart'; import '../../shared/widgets/primary_cta.dart'; import '../../shared/widgets/screen_insets.dart'; class StepNetworkTest extends ConsumerStatefulWidget { const StepNetworkTest({ super.key, required this.match, required this.onBack, }); final MatchModel match; final VoidCallback onBack; @override ConsumerState createState() => _StepNetworkTestState(); } class _StepNetworkTestState extends ConsumerState { bool _testing = false; bool _testCompleted = false; bool _ready = false; double _downloadMbps = 0; double _uploadMbps = 0; int _latencyMs = 0; String _networkType = '—'; bool _starting = false; Future _runTest() async { setState(() { _testing = true; _ready = false; }); final connectivity = await Connectivity().checkConnectivity(); final networkLabel = _connectivityLabel(connectivity); // Simulazione test rete locale (sostituibile con speed test reale). await Future.delayed(const Duration(seconds: 2)); final rng = Random(); final download = 8 + rng.nextDouble() * 12; final upload = 2 + rng.nextDouble() * 4; final latency = 20 + rng.nextInt(80); final session = ref.read(sessionProvider).session; NetworkTestResult? testResult; if (session != null) { try { final client = ref.read(apiClientProvider); testResult = await client.networkTest( sessionId: session.id, downloadMbps: download, uploadMbps: upload, latencyMs: latency, networkType: networkLabel, ); } catch (_) { testResult = null; } } if (mounted) { setState(() { _testing = false; _testCompleted = true; _downloadMbps = download; _uploadMbps = upload; _latencyMs = latency; _networkType = networkLabel; _ready = testResult?.ready ?? upload >= 2.0; }); } } String _connectivityLabel(List results) { if (results.contains(ConnectivityResult.wifi)) return 'WiFi'; if (results.contains(ConnectivityResult.mobile)) return '4G'; if (results.contains(ConnectivityResult.ethernet)) return 'Ethernet'; return 'Sconosciuto'; } Future _startLive() async { final sessionState = ref.read(sessionProvider); final session = sessionState.session; if (session == null) return; setState(() => _starting = true); try { final client = ref.read(apiClientProvider); final started = await client.startSession(session.id); ref.read(sessionProvider.notifier).setSession(started, match: widget.match); if (started.score != null) { ref.read(scoreProvider.notifier).reset(started.score!); } else { ref.read(scoreProvider.notifier).reset(const ScoreState()); } if (!mounted) return; context.go('/session/${started.id}/camera'); try { final ws = ref.read(websocketServiceProvider); await ws.connect(sessionId: started.id, deviceRole: 'camera'); } catch (e) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Diretta avviata ma sync limitata: $e')), ); } } } catch (e) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Errore avvio diretta: $e')), ); } } finally { if (mounted) setState(() => _starting = false); } } @override Widget build(BuildContext context) { final theme = Theme.of(context); final session = ref.watch(sessionProvider).session; final shareUrl = session != null ? watchShareUrl(session) : null; final shareTitle = 'Diretta — ${widget.match.teamName} vs ${widget.match.opponentName}'; return SingleChildScrollView( padding: ScreenInsets.contentPadding(context, horizontal: 20, vertical: 12), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Text('Test rete', style: theme.textTheme.titleLarge), const SizedBox(height: 8), Text( 'Verifica che la connessione regga l\'upload della diretta.', style: theme.textTheme.bodyMedium, ), const SizedBox(height: 24), Row( children: [ MetricCard( label: 'Download', value: _testing ? '...' : '${_downloadMbps.toStringAsFixed(1)} Mbps', icon: Icons.download, ), const SizedBox(width: 8), MetricCard( label: 'Upload', value: _testing ? '...' : '${_uploadMbps.toStringAsFixed(1)} Mbps', icon: Icons.upload, highlight: _ready, ), const SizedBox(width: 8), MetricCard( label: 'Latenza', value: _testing ? '...' : '${_latencyMs} ms', icon: Icons.speed, ), ], ), const SizedBox(height: 12), SizedBox( width: double.infinity, child: MetricCard( label: 'Tipo rete', value: _networkType, icon: Icons.signal_cellular_alt, expand: false, ), ), const SizedBox(height: 20), if (_testCompleted && _ready) ...[ Container( padding: const EdgeInsets.all(14), decoration: BoxDecoration( color: AppTheme.successGreen.withValues(alpha: 0.15), borderRadius: BorderRadius.circular(10), border: Border.all(color: AppTheme.successGreen.withValues(alpha: 0.4)), ), child: Text( 'PRONTO PER ANDARE IN DIRETTA', style: theme.textTheme.titleMedium?.copyWith( color: AppTheme.successGreen, ), textAlign: TextAlign.center, ), ), const SizedBox(height: 12), ], if (_testCompleted && session != null && shareUrl != null) ...[ Container( padding: const EdgeInsets.all(14), decoration: BoxDecoration( color: AppTheme.surfaceElevated, borderRadius: BorderRadius.circular(10), ), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Text( session.platform == 'youtube' ? 'Link video YouTube' : 'Link diretta Match Live TV', style: theme.textTheme.titleSmall, ), const SizedBox(height: 8), SelectableText( shareUrl, style: theme.textTheme.bodySmall, ), const SizedBox(height: 8), Row( children: [ Expanded( child: OutlinedButton.icon( onPressed: () => copyWatchLink(context, session), icon: const Icon(Icons.link, size: 18), label: const Text('COPIA'), ), ), const SizedBox(width: 8), Expanded( child: OutlinedButton.icon( onPressed: () => shareWatchLink( context, session: session, title: shareTitle, ), icon: const Icon(Icons.share, size: 18), label: const Text('CONDIVIDI'), ), ), ], ), ], ), ), const SizedBox(height: 8), OutlinedButton.icon( onPressed: () => shareRegiaLink( context, ref, sessionId: session.id, shareSubject: 'Regia — ${widget.match.teamName} vs ${widget.match.opponentName}', ), icon: const Icon(Icons.scoreboard), label: const Text('Condividi link regia (punteggio)'), ), ], if (_testCompleted) ...[ const SizedBox(height: 16), Container( padding: const EdgeInsets.all(14), decoration: BoxDecoration( color: AppTheme.surfaceElevated, borderRadius: BorderRadius.circular(10), ), child: Text( 'In caso di disconnessione, lo stream resta attivo con slate per massimo 5 minuti mentre il telefono riconnette.', style: theme.textTheme.bodyMedium, ), ), ], if (!_testCompleted) ...[ const SizedBox(height: 24), OutlinedButton( onPressed: _testing ? null : _runTest, child: Text(_testing ? 'TEST IN CORSO...' : 'AVVIA TEST RETE'), ), ], const SizedBox(height: 32), Row( children: [ Expanded( child: OutlinedButton( onPressed: _starting ? null : widget.onBack, child: const Text('Indietro'), ), ), const SizedBox(width: 12), Expanded( flex: 2, child: PrimaryCta( label: 'INIZIA >', loading: _starting, onPressed: _ready ? _startLive : null, icon: Icons.play_arrow, ), ), ], ), ], ), ); } }