import 'dart:math' as math; import 'package:flutter/foundation.dart'; import 'package:relationship_saver/features/sync/sync_coordinator.dart'; typedef SyncNowRunner = Future Function(); /// Coordinates auto-triggered sync calls with cooldown and concurrency guards. class SyncAutoTriggerController { SyncAutoTriggerController({ required SyncNowRunner syncNow, required DateTime Function() now, this.minimumInterval = const Duration(minutes: 3), }) : _syncNow = syncNow, _now = now; final SyncNowRunner _syncNow; final DateTime Function() _now; final Duration minimumInterval; bool _running = false; DateTime? _lastTriggeredAt; int _consecutiveFailures = 0; @visibleForTesting bool get isRunning => _running; @visibleForTesting DateTime? get lastTriggeredAt => _lastTriggeredAt; @visibleForTesting int get consecutiveFailures => _consecutiveFailures; /// Triggers `syncNow` when not already running and outside cooldown window. Future trigger({bool ignoreCooldown = false}) async { if (_running) { return false; } final DateTime now = _now(); final DateTime? last = _lastTriggeredAt; if (!ignoreCooldown && last != null && now.difference(last) < _cooldownForCurrentState()) { return false; } _running = true; _lastTriggeredAt = now; try { final SyncRunResult result = await _syncNow(); if (result.success) { _consecutiveFailures = 0; } else { _consecutiveFailures += 1; } return true; } catch (_) { _consecutiveFailures += 1; return true; } finally { _running = false; } } Duration _cooldownForCurrentState() { if (_consecutiveFailures <= 0) { return minimumInterval; } final int cappedFailures = math.min(_consecutiveFailures, 4); final int multiplier = 1 << cappedFailures; return minimumInterval * multiplier; } }