package sync import ( "context" "fmt" "go.uber.org/zap" "codeberg.org/mapleopentech/monorepo/native/desktop/maplefile/internal/domain/syncstate" ) // Service provides unified sync operations type Service interface { // SyncAll synchronizes both collections and files SyncAll(ctx context.Context, input *SyncInput) (*SyncResult, error) // SyncCollections synchronizes collections only SyncCollections(ctx context.Context, input *SyncInput) (*SyncResult, error) // SyncFiles synchronizes files only SyncFiles(ctx context.Context, input *SyncInput) (*SyncResult, error) // GetSyncStatus returns the current sync status GetSyncStatus(ctx context.Context) (*SyncStatus, error) // ResetSync resets all sync state for a fresh sync ResetSync(ctx context.Context) error } type service struct { logger *zap.Logger collectionSync CollectionSyncService fileSync FileSyncService repoProvider RepositoryProvider } // ProvideService creates a new unified sync service for Wire func ProvideService( logger *zap.Logger, collectionSync CollectionSyncService, fileSync FileSyncService, repoProvider RepositoryProvider, ) Service { return &service{ logger: logger.Named("SyncService"), collectionSync: collectionSync, fileSync: fileSync, repoProvider: repoProvider, } } // getSyncStateRepo returns the sync state repository, or an error if not initialized func (s *service) getSyncStateRepo() (syncstate.Repository, error) { if !s.repoProvider.IsInitialized() { return nil, fmt.Errorf("storage not initialized - user must be logged in") } repo := s.repoProvider.GetSyncStateRepository() if repo == nil { return nil, fmt.Errorf("sync state repository not available") } return repo, nil } // SyncAll synchronizes both collections and files func (s *service) SyncAll(ctx context.Context, input *SyncInput) (*SyncResult, error) { s.logger.Info("Starting full sync (collections + files)") // Sync collections first colResult, err := s.collectionSync.Execute(ctx, input) if err != nil { s.logger.Error("Collection sync failed during full sync", zap.Error(err)) return nil, err } // Sync files fileResult, err := s.fileSync.Execute(ctx, input) if err != nil { s.logger.Error("File sync failed during full sync", zap.Error(err)) // Return partial result with collection data return &SyncResult{ CollectionsProcessed: colResult.CollectionsProcessed, CollectionsAdded: colResult.CollectionsAdded, CollectionsUpdated: colResult.CollectionsUpdated, CollectionsDeleted: colResult.CollectionsDeleted, Errors: append(colResult.Errors, "file sync failed: "+err.Error()), }, err } // Merge results result := &SyncResult{ CollectionsProcessed: colResult.CollectionsProcessed, CollectionsAdded: colResult.CollectionsAdded, CollectionsUpdated: colResult.CollectionsUpdated, CollectionsDeleted: colResult.CollectionsDeleted, FilesProcessed: fileResult.FilesProcessed, FilesAdded: fileResult.FilesAdded, FilesUpdated: fileResult.FilesUpdated, FilesDeleted: fileResult.FilesDeleted, Errors: append(colResult.Errors, fileResult.Errors...), } s.logger.Info("Full sync completed", zap.Int("collections_processed", result.CollectionsProcessed), zap.Int("files_processed", result.FilesProcessed), zap.Int("errors", len(result.Errors))) return result, nil } // SyncCollections synchronizes collections only func (s *service) SyncCollections(ctx context.Context, input *SyncInput) (*SyncResult, error) { return s.collectionSync.Execute(ctx, input) } // SyncFiles synchronizes files only func (s *service) SyncFiles(ctx context.Context, input *SyncInput) (*SyncResult, error) { return s.fileSync.Execute(ctx, input) } // GetSyncStatus returns the current sync status func (s *service) GetSyncStatus(ctx context.Context) (*SyncStatus, error) { syncStateRepo, err := s.getSyncStateRepo() if err != nil { return nil, err } state, err := syncStateRepo.Get() if err != nil { return nil, err } return &SyncStatus{ CollectionsSynced: state.IsCollectionSyncComplete(), FilesSynced: state.IsFileSyncComplete(), FullySynced: state.IsFullySynced(), }, nil } // ResetSync resets all sync state for a fresh sync func (s *service) ResetSync(ctx context.Context) error { s.logger.Info("Resetting sync state") syncStateRepo, err := s.getSyncStateRepo() if err != nil { return err } return syncStateRepo.Reset() }