import 'dart:async'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../shared/api_client.dart'; import '../shared/models/score_state.dart'; import 'score_provider.dart'; import 'session_provider.dart'; /// Coda sync HTTP: evita richieste fuori ordine e allinea l'app al DB (pagina live). class ScoreSyncService { ScoreSyncService(this._ref); final Ref _ref; bool _busy = false; bool _dirty = false; DateTime? _suppressStaleRemoteUntil; /// Ignora broadcast WebSocket obsoleti subito dopo un push HTTP. bool acceptCableScore(ScoreState remote) { final until = _suppressStaleRemoteUntil; if (until == null || DateTime.now().isAfter(until)) return true; final local = _ref.read(scoreProvider); return !_isClearlyBehind(remote, local); } bool _isClearlyBehind(ScoreState remote, ScoreState local) { final r = remote.homeSets * 1000 + remote.awaySets * 1000 + remote.homePoints + remote.awayPoints; final l = local.homeSets * 1000 + local.awaySets * 1000 + local.homePoints + local.awayPoints; return r < l; } Future push() async { _dirty = true; if (_busy) return; _busy = true; while (_dirty) { _dirty = false; final sessionId = _ref.read(sessionProvider).session?.id; if (sessionId == null) continue; final payload = _ref.read(scoreProvider).toCablePayload(); try { final remote = await _ref.read(apiClientProvider).syncScore(sessionId, payload); _suppressStaleRemoteUntil = DateTime.now().add(const Duration(milliseconds: 500)); if (remote != null) { _ref.read(scoreProvider.notifier).applyRemote(remote); } } catch (_) { // Mantieni stato locale; il prossimo pull periodico riallinea se possibile. } } _busy = false; } Future pullFromServer() async { if (_busy) return; final sessionId = _ref.read(sessionProvider).session?.id; if (sessionId == null) return; try { final session = await _ref.read(apiClientProvider).fetchSession(sessionId); if (session.score != null) { _ref.read(scoreProvider.notifier).applyRemote(session.score!); } } catch (_) {} } } final scoreSyncProvider = Provider((ref) { return ScoreSyncService(ref); });