using System; using System.Collections.Generic; using System.Drawing.Drawing2D; using System.Linq; using System.Text; using System.Text.Json; using System.Threading.Tasks; using gehGassi.Dto.Common; using gehGassi.Dto; using gehGassiApp.Core.Data; using gehGassiApp.Core.Interfaces; using gehGassiApp.Core.Interfaces.Synchronization; using gehGassiApp.Core.Mapper; using gehGassiApp.Core.Resources; using gehGassiApp.Domain.Common; using gehGassiApp.Domain.Favourites; using gehGassiApp.Domain.Users; using gehGassi.Dto.Favourites; namespace gehGassiApp.Core.Services { /// /// Service der die Verwaltung von Favoriten ermöglicht /// public class FavouriteService : ServiceBase, IFavouriteService { private readonly ICommunicationService _communicationService; private readonly ISyncInfoPullService _pullService; private readonly ISyncInfoPushService _pushService; private readonly ILocationService _locationService; private readonly IRepository _appUserRepository; /// /// Erstellt eine Instanz /// /// Instanz eines IUnitOfWork /// Instanz eines ICommunicationService /// Instanz eines ISyncInfoPullService /// Instanz eines ISyncInfoPushService /// Instanz eines ILocationService public FavouriteService(IUnitOfWork unitOfWork, ICommunicationService communicationService, ISyncInfoPullService syncInfoPullService, ISyncInfoPushService syncInfoPushService, ILocationService locationService) : base(unitOfWork) { _communicationService = communicationService; _pullService = syncInfoPullService; _pushService = syncInfoPushService; _locationService = locationService; _appUserRepository = unitOfWork.GetRepository(); } public override Favourite Get(object id, bool noTracking = true) { throw new NotImplementedException(); } public override async Task GetAsync(object id, bool noTracking = true) { await Task.Delay(1); throw new NotImplementedException(); } /// /// Erstellen eines Favoriten /// /// Favourite public Favourite Create() { var favourite = new Favourite() { Id = Guid.NewGuid().ToString("N"), Created = DateTimeOffset.UtcNow }; return favourite; } /// /// Hinzufügen eines Favoriten /// /// Favourite /// Aktuelles Accesstoken /// CancellationToken /// Angelegter Favorit public async Task AddAsync(Favourite favourite, string accessToken, CancellationToken token) { var existingFavourite = await Repository.GetAsync(favourite.Id); if (existingFavourite == null) { Repository.Add(favourite); await CommitAsync(); //Jetzt das Rating an der Server übertragen oder ein Delta erstellen var dto = favourite.ToDto(); var isConnected = await _communicationService.IsConnected(); var createDelta = true; if (isConnected) { var createResult = await _communicationService.CreateFavouriteAsync(dto, accessToken, token); if (createResult.Success && createResult.Value.Status != CreateStatus.Error) { createDelta = false; } } if (createDelta) { await _pushService.AddAsync(nameof(Favourite), favourite.Id, SyncOperation.Create, favourite).ConfigureAwait(false); } return favourite; } return null; } /// /// Aktualisieren eines Favoriten /// /// Favourite /// Aktuelles Accesstoken /// CancellationToken /// true wenn erfolgreich, false sonst public async Task UpdateAsync(Favourite favourite, string accessToken, CancellationToken token) { var localFavourite = await Repository.FirstOrDefaultAsync(c => c.Id == favourite.Id, false).ConfigureAwait(false); if (localFavourite != null && !localFavourite.Deleted) { localFavourite.AppUserId = favourite.AppUserId; localFavourite.Table = favourite.Table; localFavourite.Key = favourite.Key; localFavourite.UpdatedAt = DateTimeOffset.UtcNow; Repository.Update(localFavourite); await CommitAsync().ConfigureAwait(false); var dto = localFavourite.ToDto(); var isConnected = await _communicationService.IsConnected(); var createDelta = true; if (isConnected) { var updateResult = await _communicationService.UpdateFavouriteAsync(dto, accessToken, token); if (updateResult.Success && updateResult.Value) createDelta = false; } if (createDelta) { await _pushService.AddAsync(nameof(Favourite), localFavourite.Id, SyncOperation.Edit, localFavourite).ConfigureAwait(false); } return true; } return false; } /// /// Echtes Löschen eines Favoriten /// /// Id des Favoriten /// Id des AppBenutzers /// Aktuelles Accesstoken /// CancellationToken /// true wenn erfolgreich, false sonst public async Task DeleteAsync(string favouriteId, string appUserId, string accessToken, CancellationToken token) { var localFavourite = await Repository.FirstOrDefaultAsync(c => c.Id == favouriteId, false).ConfigureAwait(false); if (localFavourite != null && localFavourite.AppUserId == appUserId) { Repository.Remove(localFavourite); await CommitAsync().ConfigureAwait(false); var dto = localFavourite.ToDto(); var isConnected = await _communicationService.IsConnected(); var createDelta = true; if (isConnected) { var deleteResult = await _communicationService.DeleteFavouriteAsync(dto, appUserId, accessToken, token); if (deleteResult.Success && deleteResult.Value) createDelta = false; } if (createDelta) { await _pushService.AddAsync(nameof(Favourite), localFavourite.Id, SyncOperation.Delete, localFavourite).ConfigureAwait(false); } return true; } return false; } /// /// Prüfen ob ein Objekt ein Favorit ist. Prüft nur Lokal! /// /// Id des Objekts /// Typ des Objekts /// true wenn ein Favorit, false sonst public async Task IsFavouriteLocalAsync(string key, string table) { var existing = await Repository.FirstOrDefaultAsync(c => c.Key == key && c.Table == table && c.Deleted == false); return existing != null; } /// /// Toggeln des Favoriten-Status eines Objektes. /// Entweder wird der Favorit angelegt, oder gelöscht /// /// Id des Objekts /// Typ des Objekts /// Id des AppUsers /// Aktuelles Accesstoken /// CancellationToken /// true wenn erfolgreich, false sonst public async Task ToggleAsync(string key, string table, string appUserId, string accessToken, CancellationToken token) { var existing = await Repository.FirstOrDefaultAsync(c => c.Key == key && c.Table == table && c.Deleted == false); if (existing != null) { //Favorit existiert, also löschen return await DeleteAsync(existing.Id, appUserId, accessToken, token); } else { //Favorit existiert nicht, also anlegen var favourite = Create(); favourite.Key = key; favourite.Table = table; favourite.AppUserId = appUserId; favourite.Created = DateTimeOffset.UtcNow; favourite.UpdatedAt = DateTimeOffset.UtcNow; var created = await AddAsync(favourite, accessToken, token); return created != null; } } /// /// Gibt eine Liste von Favoriten für eine Objektklasse für einen Benutzer zurück /// /// Gewünschtes Objekt /// Aktuelle Position /// Gewünschte Sprache /// Aktuelles Accesstoken /// CancellationToken /// Id des AppUsers /// ListCommunicationResult public async Task>> GetListAsync(string appUserId, string table, LocationDto location, string language, string accessToken, CancellationToken token) { var result = new CommunicationResult>() { Value = new List() }; var isConnected = await _communicationService.IsConnected(); if (isConnected) { var query = new FavouriteListQueryDto { Location =location, Language = language, AppUserId = appUserId, Table = table }; var queryResult = await _communicationService.GetFavouritesListAsync(query, accessToken, token); if (queryResult.Success) { result.Success = true; result.Value = queryResult.Value; } } else { result.Success = false; result.ErrorCode = CommunicationErrors.ServerNoConnection; result.ErrorMessage = Errors.Server_NoConnection; } return result; } #region Pull-Push Implementation /// /// Holen der letzten Daten vom Server und Synchronisieren mit den lokalen Daten /// /// Sprache /// Aktuelles Accesstoken /// CancellationToken /// Task public async Task> PullAsync(string language, string accessToken, CancellationToken token) { var result = new SyncResult(); var isConnected = await _communicationService.IsConnected(); if (isConnected) { var user = await _appUserRepository.FirstOrDefaultAsync(c => c.Id != "", false).ConfigureAwait(false); //Zuerst lezte Aktivität holen... DateTimeOffset? lastUpdate = null; var lastSyncInfo = await _pullService.GetAsync(nameof(Favourite)); if (lastSyncInfo != null) lastUpdate = lastSyncInfo.LastUpdate; var requestResult = await _communicationService.GetFavouritesForSyncAsync(user.Id, lastUpdate, accessToken, token); if (requestResult.Success) { result = await HandleChangesAsync(requestResult.Value); if (result.LastUpdate != null) await _pullService.AddOrUpddateAsync(nameof(Favourite), result.LastUpdate.Value).ConfigureAwait(false); } result.LastUpdate ??= lastUpdate; } return result; } /// /// Holen der Daten vom lokalen Speicher die noch nicht synchronisiert wurden und senden an den Server /// /// Aktuelles Accesstoken /// CancellationToken /// Task public async Task> PushAsync(string accessToken, CancellationToken token) { var result = new SyncResult(); if (await _pushService.HasOpenAsync(nameof(Favourite)) > 0) { var deltas = await _pushService.GetAllAsync(nameof(Favourite)); if (deltas.Any()) { var user = await _appUserRepository.FirstOrDefaultAsync(c => c.Id != "", false).ConfigureAwait(false); deltas = deltas.OrderBy(c => c.DateTime).ToList(); var isConnected = await _communicationService.IsConnected(); if (!isConnected) return result; foreach (var syncInfoPush in deltas) { try { if (!string.IsNullOrWhiteSpace(syncInfoPush.Value)) { var request = JsonSerializer.Deserialize(syncInfoPush.Value, new JsonSerializerOptions(JsonSerializerDefaults.Web)); if (syncInfoPush.Operation == SyncOperation.Create) { var createDto = request.ToDto(); var createResult = await _communicationService.CreateFavouriteAsync(createDto, accessToken, token); if (createResult.Success && createResult.Value.Status != CreateStatus.Error) { await _pushService.RemoveAsync(syncInfoPush.Id).ConfigureAwait(false); } //TODO: Was tun wenn Fehler? } else if (syncInfoPush.Operation == SyncOperation.Edit) { var updateDto = request.ToDto(); var updateResult = await _communicationService.UpdateFavouriteAsync(updateDto, accessToken, token); if (updateResult.Success && updateResult.Value) await _pushService.RemoveAsync(syncInfoPush.Id).ConfigureAwait(false); //TODO: Was tun wenn Fehler? } else { var deleteDto = request.ToDto(); var deleteResult = await _communicationService.DeleteFavouriteAsync(deleteDto, user.Id, accessToken, token); if (deleteResult.Success && deleteResult.Value) await _pushService.RemoveAsync(syncInfoPush.Id).ConfigureAwait(false); //TODO: Was tun wenn Fehler? } } else { await _pushService.RemoveAsync(syncInfoPush.Id).ConfigureAwait(false); } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex.Message); await _pushService.RemoveAsync(syncInfoPush.Id).ConfigureAwait(false); } } //Else ist nichts tun, konnte nicht übertragen werden. } } return result; } #endregion #region Private /// /// Behandeln der Liste von Favoriten wenn welche vom Online-Store geholt werden. /// /// Liste der Favoriten /// Task private async Task> HandleChangesAsync(List favourites) { var result = new SyncResult(); //Je Favorit durchgehen ob was gemacht werden soll foreach (var favourite in favourites) { var localFavourite = await Repository.FirstOrDefaultAsync(c => c.Id == favourite.Id, false); if (localFavourite != null && localFavourite.UpdatedAt < favourite.UpdatedAt) { if (localFavourite.UpdatedAt >= favourite.UpdatedAt) { if (result.LastUpdate == null || result.LastUpdate < localFavourite.UpdatedAt) result.LastUpdate = localFavourite.UpdatedAt; continue; } var deleted = localFavourite.Deleted == false && favourite.Deleted; localFavourite.Version = favourite.Version; localFavourite.UpdatedAt = favourite.UpdatedAt; localFavourite.Deleted = favourite.Deleted; localFavourite.AppUserId = favourite.AppUserId; localFavourite.Key = favourite.Key; localFavourite.Table = favourite.Table; localFavourite.Created = favourite.Created; if (result.LastUpdate == null || result.LastUpdate < localFavourite.UpdatedAt) result.LastUpdate = localFavourite.UpdatedAt; if (!deleted) result.Updated.Add(localFavourite); else result.Deleted.Add(localFavourite); Repository.Update(localFavourite); } if (localFavourite == null) { localFavourite = new Favourite() { Id = favourite.Id, Version = favourite.Version, UpdatedAt = favourite.UpdatedAt, Deleted = favourite.Deleted, AppUserId = favourite.AppUserId, Key = favourite.Key, Table = favourite.Table, Created = favourite.Created }; Repository.Add(localFavourite); if (result.LastUpdate == null || result.LastUpdate < localFavourite.UpdatedAt) result.LastUpdate = localFavourite.UpdatedAt; result.Added.Add(localFavourite); } } if (result.HasChanges) { try { await CommitAsync().ConfigureAwait(false); } catch (Exception ex) { var err = ex.Message; } } return result; } #endregion } }