import 'package:flutter/foundation.dart'; import 'package:aves/model/source/collection_source.dart'; import 'remote_http_api.dart'; import 'remote_models.dart'; import 'remote_repository.dart'; import 'remote_state_store.dart'; import 'package:aves/remote/collection_source_remote_ext.dart'; class RemoteSyncEngine { RemoteSyncEngine({ required this.api, required this.repo, required this.source, required this.state, }); final RemoteHttpApi api; final RemoteRepository repo; final CollectionSource source; final RemoteStateStore state; static const int retentionDays = 30; bool _isTooOld(String iso) { final t = DateTime.tryParse(iso); if (t == null) return true; return DateTime.now().difference(t).inDays > retentionDays; } Future progressiveSync() async { final last = await state.getLastSyncIso(); if (last == null || last.isEmpty || _isTooOld(last)) { await fullSync(); return; } await deltaSyncFrom(last); await state.setLastSyncIso(DateTime.now().toUtc().toIso8601String()); } Future progressiveSyncFrom(String sinceIso) async { if (_isTooOld(sinceIso)) { await fullSync(); return; } await deltaSyncFrom(sinceIso); await state.setLastSyncIso(DateTime.now().toUtc().toIso8601String()); } Future fullSync() async { debugPrint('[remote] fullSync start'); final list = await api.getAllPhotos(); final items = list.map(RemotePhotoItem.fromJson).toList(); await repo.deleteAllRemotes(); await repo.upsertAll(items, chunkSize: 200); await source.appendRemoteEntriesFromDb(); await state.setLastSyncIso(DateTime.now().toUtc().toIso8601String()); debugPrint('[remote] fullSync done items=${items.length}'); } /// Delta sync: upsert changed + delete hardDeleted. /// [appendAfter]=false utile se vuoi batchare più operazioni e appendare una volta sola. Future deltaSyncFrom(String sinceIso, {bool appendAfter = true}) async { debugPrint('[remote] deltaSyncFrom since=$sinceIso'); List> changed = const []; List> deleted = const []; // più resiliente: separo i due fetch try { changed = await api.getChanges(sinceIso); } catch (e) { debugPrint('[remote] getChanges failed: $e'); // lasciamo che il chiamante decida (controller può fare recovery) rethrow; } try { deleted = await api.getDeletedHard(sinceIso); } catch (e) { debugPrint('[remote] getDeletedHard failed: $e'); // anche qui: rethrow per recovery (coerente con retry WS) rethrow; } var didChange = false; if (changed.isNotEmpty) { final items = changed.map(RemotePhotoItem.fromJson).toList(); await repo.upsertAll(items, chunkSize: 200); debugPrint('[remote] delta upsert=${items.length}'); didChange = true; } if (deleted.isNotEmpty) { final ids = deleted.map((e) => e['id']?.toString()).whereType().toSet(); if (ids.isNotEmpty) { await repo.deleteRemotesByRemoteIds(ids); debugPrint('[remote] delta hardDeleted=${ids.length}'); didChange = true; } } // ✅ append solo se necessario e richiesto if (appendAfter && didChange) { await source.appendRemoteEntriesFromDb(); } } Future upsertOneFromJson(Map json) async { final it = RemotePhotoItem.fromJson(json); await repo.upsertAll([it], chunkSize: 1); } Future deleteRemoteIds(Set ids) async { await repo.deleteRemotesByRemoteIds(ids); } }