using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using gehGassi.Dto;
using gehGassi.Dto.DogWalkers;
using gehGassiApp.Core.Data;
using gehGassiApp.Core.Interfaces;
using gehGassiApp.Core.Interfaces.Synchronization;
using gehGassiApp.Core.Mapper;
using gehGassiApp.Domain.Common;
using gehGassiApp.Domain.Users;
namespace gehGassiApp.Core.Services
{
///
/// Service der die Verwaltung von Walker-Profilen ermöglicht
///
public class WalkerProfileService : ServiceBase, IWalkerProfileService
{
private readonly ICommunicationService _communicationService;
private readonly ISyncInfoPushService _pushService;
private readonly ISyncInfoPullService _pullService;
///
/// Erstellt eine Instanz
///
/// Instanz eines IUnitOfWork
/// Instanz eines ICommunicationService
/// Instanz eines ISyncInfoPushService
/// Instanz eines ISyncInfoPullService
public WalkerProfileService(IUnitOfWork unitOfWork, ICommunicationService communicationService, ISyncInfoPushService pushService, ISyncInfoPullService pullService) : base(unitOfWork)
{
_communicationService = communicationService;
_pushService = pushService;
_pullService = pullService;
}
///
/// Gibt eine Entität anhand der eindeutigen Id zurück
///
/// Id der Entität
/// Gibt an ob NoTRacking verwendet werden soll. Es werden keine Entitäten im EF-Speicher gehalten
/// Entität oder null, wenn nicht gefunden
public override DogWalkerProfile Get(object id, bool noTracking = true)
{
//Bewusst nicht implementiert
throw new NotImplementedException();
}
///
/// Gibt eine Entität anhand der eindeutigen Id zurück
///
/// Id der Entität
/// Gibt an ob NoTRacking verwendet werden soll. Es werden keine Entitäten im EF-Speicher gehalten
/// Entität oder null, wenn nicht gefunden
public override async Task GetAsync(object id, bool noTracking = true)
{
//Bewusst nicht implementiert
await Task.Delay(1);
throw new NotImplementedException();
}
///
/// Gibt das aktuelle Walkerprofil zurück.
///
/// DogWalkerProfile oder null wenn nicht gefunden
public async Task GetAsync()
{
var profile = await Repository.FirstOrDefaultAsync(c => c.Id != "", false).ConfigureAwait(false);
return profile;
}
///
/// Gibt das aktuelle Walkerprofil zurück.
/// Versucht auch online - wenn lokal nicht vorhanden
///
/// Aktuelles Accesstoken
/// CancellationToken
/// Wenn true, wird in jedem Fall die Online-Abfrage verwendet
/// DogWalkerProfile oder null wenn nicht gefunden
public async Task GetAsync(string accessToken, CancellationToken token, bool forceOnline)
{
//Zuerst Online wenn gewünscht
if (forceOnline)
{
var isConnected = await _communicationService.IsConnected();
if (isConnected)
{
var profileResult = await _communicationService.GetWalkerProfileAsync(accessToken, token);
if (profileResult.Success)
{
var profileUpdated = await CreateOrUpdateAsync(profileResult.Value);
await _pullService.AddOrUpddateAsync(nameof(DogWalkerProfile), DateTimeOffset.UtcNow);
return profileUpdated;
}
}
}
var profile = await Repository.FirstOrDefaultAsync(c => c.Id != "").ConfigureAwait(false);
if (profile != null)
return profile;
return null;
}
///
/// Hinzufügen oder aktualisieren eines Walkerprofils
///
/// Walker-Profil
/// AppUser
public async Task CreateOrUpdateAsync(DogWalkerProfile walkerProfile)
{
var foundProfile = await Repository.FirstOrDefaultAsync(c => c.Id != "", true).ConfigureAwait(false);
if (foundProfile == null)
{
Repository.Add(walkerProfile);
await CommitAsync();
return walkerProfile;
}
if (walkerProfile.UpdatedAt > foundProfile.UpdatedAt)
{
foundProfile.Radius = walkerProfile.Radius;
foundProfile.About = walkerProfile.About;
foundProfile.AllowedRequests = walkerProfile.AllowedRequests;
foundProfile.PuppyAllowed = walkerProfile.PuppyAllowed;
foundProfile.DifficultAllowed = walkerProfile.DifficultAllowed;
foundProfile.AggressionLevel = walkerProfile.AggressionLevel;
foundProfile.AcceptedSize = walkerProfile.AcceptedSize;
foundProfile.PriceWalk = walkerProfile.PriceWalk;
foundProfile.PriceDay = walkerProfile.PriceDay;
foundProfile.PriceSitting = walkerProfile.PriceSitting;
foundProfile.NotAvailable = walkerProfile.NotAvailable;
foundProfile.NotAvailableInfo = walkerProfile.NotAvailableInfo;
foundProfile.UpdatedAt = walkerProfile.UpdatedAt;
foundProfile.Deleted = walkerProfile.Deleted;
Repository.Update(foundProfile);
var changes = await CommitAsync().ConfigureAwait(false);
}
return await GetAsync();
}
///
/// Aktualisieren eines Walker-Profils.
/// Online wird versucht. Wenn nicht möglich wird ein Delta erstellt.
///
/// Walker-Profil
/// Aktuelles Accesstoken
/// CancellationToken
/// DogWalkerProfile
public async Task UpdateAsync(DogWalkerProfile walkerProfile, string accessToken, CancellationToken token)
{
//Zuerst in der DB aktualisieren
var foundProfile = await Repository.FirstOrDefaultAsync(c => c.Id == walkerProfile.Id, false);
foundProfile.Radius = walkerProfile.Radius;
foundProfile.About = walkerProfile.About;
foundProfile.AllowedRequests = walkerProfile.AllowedRequests;
foundProfile.PuppyAllowed = walkerProfile.PuppyAllowed;
foundProfile.DifficultAllowed = walkerProfile.DifficultAllowed;
foundProfile.AggressionLevel = walkerProfile.AggressionLevel;
foundProfile.AcceptedSize = walkerProfile.AcceptedSize;
foundProfile.PriceWalk = walkerProfile.PriceWalk;
foundProfile.PriceDay = walkerProfile.PriceDay;
foundProfile.PriceSitting = walkerProfile.PriceSitting;
foundProfile.NotAvailable = walkerProfile.NotAvailable;
foundProfile.NotAvailableInfo = walkerProfile.NotAvailableInfo;
foundProfile.UpdatedAt = DateTimeOffset.UtcNow;
Repository.Update(foundProfile);
await CommitAsync().ConfigureAwait(false);
var profileDto = foundProfile.ToDto();
var isConnected = await _communicationService.IsConnected();
if (isConnected)
{
var updateResult = await _communicationService.UpdateWalkerProfileAsync(profileDto, accessToken, token);
if (updateResult.Success)
{
//Alles gut nichts weglegen
//Für den Pull-Service das Update-Datum setzen
await _pullService.AddOrUpddateAsync(nameof(DogWalkerProfile), foundProfile.UpdatedAt).ConfigureAwait(false);
}
else
{
//Weglegen und später versuchen
await _pushService.AddAsync(nameof(DogWalkerProfile), foundProfile.Id, SyncOperation.Edit, foundProfile).ConfigureAwait(false);
}
}
else
{
//Weglegen und später versuchen
await _pushService.AddAsync(nameof(DogWalkerProfile), foundProfile.Id, SyncOperation.Edit, foundProfile).ConfigureAwait(false);
}
return foundProfile;
}
///
/// 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(DogWalkerProfile)) > 0)
{
var deltas = await _pushService.GetAllAsync(nameof(DogWalkerProfile)).ConfigureAwait(false);
if (deltas.Any())
{
var isConnected = await _communicationService.IsConnected();
if (!isConnected) return result;
deltas = deltas.OrderBy(c => c.DateTime).ToList();
foreach (var syncInfoPush in deltas)
{
//Es gibt hier ur Update... daher alles andere ignorieren
if (syncInfoPush.Operation != SyncOperation.Edit)
{
await _pushService.RemoveAsync(syncInfoPush.Id).ConfigureAwait(false);
continue;
}
try
{
if (!string.IsNullOrWhiteSpace(syncInfoPush.Value))
{
var profile = JsonSerializer.Deserialize(syncInfoPush.Value, new JsonSerializerOptions(JsonSerializerDefaults.Web));
//Jetzt Update senden...
var profileDto = profile.ToDto();
var updateResult = await _communicationService.UpdateWalkerProfileAsync(profileDto, accessToken, token);
if (updateResult.Success)
{
//Aktualisierung durchgeführt.
await _pullService.AddOrUpddateAsync(nameof(DogWalkerProfile), profile.UpdatedAt).ConfigureAwait(false);
await _pushService.RemoveAsync(syncInfoPush.Id).ConfigureAwait(false);
}
else
{
//TODO: Prüfen!!!!
//Derzeiut egal welcher Fehler vorliegt, weg damit
//Aktualisierung durchgeführt.
await _pullService.AddOrUpddateAsync(nameof(DogWalkerProfile), profile.UpdatedAt).ConfigureAwait(false);
await _pushService.RemoveAsync(syncInfoPush.Id).ConfigureAwait(false);
}
//Else ist nichts tun, konnte nicht übertragen werden.
}
else
{
await _pushService.RemoveAsync(syncInfoPush.Id).ConfigureAwait(false);
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
await _pushService.RemoveAsync(syncInfoPush.Id).ConfigureAwait(false);
}
}
}
}
//Es gibt hier kein Push!
await Task.Delay(1);
return result;
}
///
/// 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();
//Zuerst immer online...
var isConnected = await _communicationService.IsConnected();
if (isConnected)
{
//Zuerst lezte Aktivität holen...
DateTimeOffset? lastUpdate = null;
var lastSyncInfo = await _pullService.GetAsync(nameof(DogWalkerProfile)).ConfigureAwait(false);
if (lastSyncInfo != null)
lastUpdate = lastSyncInfo.LastUpdate;
var ctsQuery = new CancellationTokenSource(Common.Constants.PushPullTimeout);
var profileResult = await _communicationService.GetWalkerProfileSyncAsync(lastUpdate, accessToken, token);
if (profileResult.Success)
{
if (profileResult.Value != null)
{
var profile = await Repository.GetAsync(profileResult.Value.Id).ConfigureAwait(false);
if (profile != null)
{
profile.Radius = profileResult.Value.Radius;
profile.About = profileResult.Value.About;
profile.AllowedRequests = profileResult.Value.AllowedRequests;
profile.PuppyAllowed = profileResult.Value.PuppyAllowed;
profile.DifficultAllowed = profileResult.Value.DifficultAllowed;
profile.AggressionLevel = profileResult.Value.AggressionLevel;
profile.AcceptedSize = profileResult.Value.AcceptedSize;
profile.PriceWalk = profileResult.Value.PriceWalk;
profile.PriceDay = profileResult.Value.PriceDay;
profile.PriceSitting = profileResult.Value.PriceSitting;
profile.NotAvailable = profileResult.Value.NotAvailable;
profile.NotAvailableInfo = profileResult.Value.NotAvailableInfo;
profile.UpdatedAt = DateTimeOffset.UtcNow;
Repository.Update(profile);
await CommitAsync().ConfigureAwait(false);
result.Updated.Add(profile);
result.LastUpdate = profileResult.Value.UpdatedAt;
await _pullService.AddOrUpddateAsync(nameof(DogWalkerProfile), profileResult.Value.UpdatedAt).ConfigureAwait(false);
}
}
}
result.LastUpdate ??= lastUpdate;
}
return result;
}
}
}