using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using gehGassi.Dto.Common;
using gehGassi.Dto;
using gehGassi.Dto.Walks;
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.Users;
using gehGassiApp.Domain.Walks;
using Microsoft.EntityFrameworkCore;
namespace gehGassiApp.Core.Services
{
///
/// Service der die Verwaltung von Antworten auf öffentlichen Anfragen ermöglicht
///
public class PublicWalkResponseService : ServiceBase, IPublicWalkResponseService
{
private readonly ICommunicationService _communicationService;
private readonly ISyncInfoPullService _pullService;
private readonly ISyncInfoPushService _pushService;
private readonly ILocationService _locationService;
private readonly IRepository _appUserRepository;
private readonly IRepository _namesRepository;
///
/// Erstellt eine Instanz
///
/// Instanz eines IUnitOfWork
/// Instanz eines ICommunicationService
/// Instanz eines ISyncInfoPullService
/// Instanz eines ISyncInfoPushService
/// Instanz eines ILocationService
public PublicWalkResponseService(IUnitOfWork unitOfWork, ICommunicationService communicationService, ISyncInfoPullService syncInfoPullService, ISyncInfoPushService syncInfoPushService,
ILocationService locationService) : base(unitOfWork)
{
_communicationService = communicationService;
_pullService = syncInfoPullService;
_pushService = syncInfoPushService;
_locationService = locationService;
_appUserRepository = unitOfWork.GetRepository();
_namesRepository = unitOfWork.GetRepository();
}
///
/// 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 PublicWalkResponse Get(object id, bool noTracking = true)
{
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)
{
await Task.Delay(1);
throw new NotImplementedException();
}
///
/// Gibt eine Liste von Antworten zu einer öffentlichen Anfragen zurück
///
/// Id der öffentlichen Anfrage
/// Aktuelle Position
/// Wie viele Listungen sollen abgerufen werden? -1 Wenn nicht anwenden.
/// Wie viele Listungen sollen ausgelassen werden? -1 Wenn nicht anwenden.
/// Akteuller Modus der App
/// Gewählte Sprache
/// Aktuelles Accesstoken
/// CancellationToken
/// ListCommunicationResult
public async Task>> GetListAsync(string publicWalkRequestId, LocationDto location, int take, int skip, AppMode appMode, string language, string accessToken, CancellationToken token)
{
var result = new ListCommunicationResult>() { Value = new List() };
var isConnected = await _communicationService.IsConnected();
if (isConnected)
{
var query = new PublicWalkResponseQueryDto()
{
Location = location,
Language = language,
AppMode = (AppModeDto)appMode,
Take = take,
Skip = skip,
PublicWalkRequestId = publicWalkRequestId,
};
var queryResult = await _communicationService.GetPublicWalkresponsesAsync(query, accessToken, token);
if (queryResult.Success)
return queryResult;
}
else
{
result.Success = false;
result.ErrorCode = CommunicationErrors.ServerNoConnection;
result.ErrorMessage = Errors.Server_NoConnection;
}
return result;
}
///
/// Holen einer Antworten zu öffentlichen Anfragen
///
/// Id der öffentlichen Anfrage
/// Aktuelles Accesstoken
/// CancellationToken
/// CommunicationResult
public async Task GetAsync(string publicWalkResponseId, string accessToken, CancellationToken token)
{
var isConnected = await _communicationService.IsConnected();
if (isConnected)
{
var requestResult = await _communicationService.GetPublicWalkResponseAsync(publicWalkResponseId, accessToken, token);
if (requestResult.Success)
return requestResult.Value;
}
return null;
}
///
/// Holen einer Antworten zu öffentlichen Anfragen
///
/// Id der öffentlichen Anfrage
/// Aktuelles Accesstoken
/// CancellationToken
/// CommunicationResult
public async Task GetWithNamesAsync(string publicWalkResponseId, string accessToken, CancellationToken token)
{
var isConnected = await _communicationService.IsConnected();
if (isConnected)
{
var requestResult = await _communicationService.GetPublicWalkResponseWithNamesAsync(publicWalkResponseId, accessToken, token);
if (requestResult.Success)
return requestResult.Value;
}
return null;
}
///
/// Holen einer Antworten zu öffentlichen Anfragen
///
/// Id des AppUsers
/// Id der öffentlichen Anfrage
/// Aktuelles Accesstoken
/// CancellationToken
/// CommunicationResult
public async Task GetForWalkerAsync(string appUserId, string publicWalkRequestId, string accessToken, CancellationToken token)
{
var isConnected = await _communicationService.IsConnected();
if (isConnected)
{
var requestResult = await _communicationService.GetPublicWalkResponseForWalkerAsync(appUserId, publicWalkRequestId, accessToken, token);
if (requestResult.Success)
return requestResult.Value;
}
var response = await Repository.FirstOrDefaultAsync(c => c.DogWalkerId == appUserId && c.PublicWalkRequestId == publicWalkRequestId && c.Deleted).ConfigureAwait(false);
return response;
}
///
/// Hinzufügen einer Antwort zu einer öffentlichen Anfrage
///
/// Antwort zur Öffentliche Anfrage
/// Aktuelles Accesstoken
/// CancellationToken
/// PublicWalkRequest
public async Task> AddAsync(PublicWalkResponse response, string accessToken, CancellationToken token)
{
//Kann nur online erfolgen
var result = new CommunicationResult()
{
Success = false,
Value = null,
ErrorCode = CommunicationErrors.Undefined
};
var isConnected = await _communicationService.IsConnected();
if (isConnected)
{
var dto = response.ToDto();
var createResult = await _communicationService.CreatePublicWalkResponseAsync(dto, accessToken, token);
if (createResult.Success)
{
if (createResult.Value.Status == CreateStatus.Success && createResult.Value.Value != null)
{
Repository.Add(response);
await CommitAsync();
result.Success = true;
result.ErrorCode = CommunicationErrors.None;
result.Value = createResult.Value.Value;
}
}
else
{
if (createResult.Value.Status == CreateStatus.Error)
result.ErrorCode = CommunicationErrors.Common_Relation_Changed;
else
result.ErrorCode = CommunicationErrors.Common_Exists;
}
}
else
{
result.ErrorCode = CommunicationErrors.ServerNoConnection;
}
return result;
}
///
/// Prüft in der lokalen DB ob ein Walker für eine öffentliche Anfrage ein Angebot abgegeben hat
///
/// Id des Dogwalkers
/// Id der öffentlichen Anfrage
/// true wenn es ein Angebot gibt, false sonst
public async Task HasResponseAsync(string appUserId, string publicWalkRequestId)
{
var response = await Repository.FirstOrDefaultAsync(c => c.DogWalkerId == appUserId && c.PublicWalkRequestId == publicWalkRequestId).ConfigureAwait(false);
return response != null;
}
///
/// Aktualisieren einer Antwort zu einer öffentlichen Anfrage
///
/// Antwort zur Öffentliche Anfrage
/// Soll der Status aktualisiert werden?
/// Aktuelles Accesstoken
/// CancellationToken
/// PublicWalkRequest
public async Task UpdateAsync(PublicWalkResponse response, bool updateStatus, string accessToken, CancellationToken token)
{
var localResponse = await Repository.FirstOrDefaultAsync(c => c.Id == response.Id, false).ConfigureAwait(false);
//Es kann nach neuinstallation vorkommen, dass die lokale Version nicht mehr da ist...
if (localResponse == null)
{
Repository.Add(response);
await CommitAsync();
}
localResponse = await Repository.FirstOrDefaultAsync(c => c.Id == response.Id, false).ConfigureAwait(false);
if (localResponse != null && !localResponse.Deleted)
{
localResponse.Price = response.Price;
localResponse.Info = response.Info;
if(updateStatus)
localResponse.Status = response.Status;
localResponse.UpdatedAt = DateTimeOffset.UtcNow;
Repository.Update(localResponse);
await CommitAsync().ConfigureAwait(false);
var responseDto = localResponse.ToDto();
var isConnected = await _communicationService.IsConnected();
var createDelta = true;
if (isConnected)
{
var updateResult = await _communicationService.UpdatePublicWalkResponseAsync(responseDto, accessToken, token);
if (updateResult.Success && updateResult.Value)
createDelta = false;
}
if (createDelta)
{
await _pushService.AddAsync(nameof(PublicWalkResponse), localResponse.Id, SyncOperation.Edit, localResponse).ConfigureAwait(false);
}
return true;
}
return false;
}
///
/// Ablehnen eines Angebotes zu einer öffentlichen Anfrage
///
/// Id der Antwort
/// Id der öffentlichen Anfrage
/// Aktuelles Accesstoken
/// CancellationToken
/// true wenn erfolgreich, false sonst
public async Task DeclineAsync(string responseId, string publicWalkRequestId, string accessToken, CancellationToken token)
{
var dto = new PublicWalkResponseDeclineDto()
{
PublicWalkRequestId = publicWalkRequestId,
PublicWalkResponseId = responseId,
UpdatedAt = DateTimeOffset.UtcNow
};
//Das hier muss online funktionieren, damit es lokal gespeichert wird
var isConnected = await _communicationService.IsConnected();
if (isConnected)
{
var declinedResult = await _communicationService.DeclinePublicWalkResponseAsync(dto, accessToken, token);
if (declinedResult.Success)
{
return true;
}
}
return false;
}
///
/// Bearbeiten eines bereits gestellten Angebotes zu einer öffentlichen Anfrage
///
/// Id der Antwort
/// Id der öffentlichen Anfrage
/// Neuer Preis
/// Info zum Angebot
/// Aktuelles Accesstoken
/// CancellationToken
/// true wenn erfolgreich, false sonst
public async Task> EditAsync(string responseId, string publicWalkRequestId, decimal price, string info, string accessToken, CancellationToken token)
{
//Kann nur online erfolgen
var result = new CommunicationResult()
{
Success = false,
Value = false,
ErrorCode = CommunicationErrors.Undefined
};
var isConnected = await _communicationService.IsConnected();
if (isConnected)
{
var dto = new PublicWalkResponseEditDto()
{
PublicWalkResponseId = responseId,
PublicWalkRequestId = publicWalkRequestId,
Price = price,
Info = info,
UpdatedAt = DateTimeOffset.UtcNow
};
var updateResult = await _communicationService.EditPublicWalkResponseAsync(dto, accessToken, token);
if (updateResult.Success)
{
if (updateResult.Value)
{
var response = await Repository.GetAsync(responseId);
if (response != null)
{
response.Price = price;
response.Info = info;
response.UpdatedAt = dto.UpdatedAt;
Repository.Update(response);
await CommitAsync();
}
result.Success = true;
result.ErrorCode = CommunicationErrors.None;
result.Value = true;
}
}
else
{
result.Success = false;
result.ErrorCode = updateResult.ErrorCode;
result.ErrorMessage = updateResult.ErrorMessage;
}
}
else
{
result.ErrorCode = CommunicationErrors.ServerNoConnection;
}
return result;
}
///
/// Bearbeiten eines bereits gestellten Angebotes zu einer öffentlichen Anfrage
///
/// Id der Antwort
/// Id der öffentlichen Anfrage
/// Grund für die Stornierung
/// Aktuelles Accesstoken
/// CancellationToken
/// true wenn erfolgreich, false sonst
public async Task> CancelAsync(string responseId, string publicWalkRequestId, string cancellationReason, string accessToken, CancellationToken token)
{
//Kann nur online erfolgen
var result = new CommunicationResult()
{
Success = false,
Value = false,
ErrorCode = CommunicationErrors.Undefined
};
var isConnected = await _communicationService.IsConnected();
if (isConnected)
{
var dto = new PublicWalkResponseCancelDto()
{
PublicWalkResponseId = responseId,
PublicWalkRequestId = publicWalkRequestId,
CancellationReason = cancellationReason,
UpdatedAt = DateTimeOffset.UtcNow
};
var updateResult = await _communicationService.CancelPublicWalkResponseAsync(dto, accessToken, token);
if (updateResult.Success)
{
if (updateResult.Value)
{
var response = await Repository.GetAsync(responseId);
if (response != null)
{
response.Status = PublicWalkResponseStatus.Cancelled;
response.CancellationReason = cancellationReason;
response.UpdatedAt = dto.UpdatedAt;
Repository.Update(response);
await CommitAsync();
}
result.Success = true;
result.ErrorCode = CommunicationErrors.None;
result.Value = true;
}
}
else
{
result.Success = false;
result.ErrorCode = updateResult.ErrorCode;
result.ErrorMessage = updateResult.ErrorMessage;
}
}
else
{
result.ErrorCode = CommunicationErrors.ServerNoConnection;
}
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(PublicWalkResponse)) > 0)
{
var deltas = await _pushService.GetAllAsync(nameof(PublicWalkResponse));
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.CreatePublicWalkResponseAsync(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.UpdatePublicWalkResponseAsync(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.DeletePublicWalkResponseAsync(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;
}
///
/// 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(PublicWalkResponse));
if (lastSyncInfo != null)
lastUpdate = lastSyncInfo.LastUpdate;
var requestResult = await _communicationService.GetPublicWalkResponsesForSyncAsync(user.Id, lastUpdate, accessToken, token);
if (requestResult.Success)
{
result = await HandleChangesAsync(requestResult.Value);
if (result.LastUpdate != null)
await _pullService.AddOrUpddateAsync(nameof(PublicWalkResponse), result.LastUpdate.Value).ConfigureAwait(false);
}
result.LastUpdate ??= lastUpdate;
}
return result;
}
#region Private
///
/// Behandeln der Liste von öffentlichern Anfragen wenn welche vom Online-Store geholt werden.
///
/// Liste der anfragen
/// Task
private async Task> HandleChangesAsync(List responses)
{
var result = new SyncResult();
//Je Rasse durchgehen ob was gemacht werden soll
foreach (var response in responses)
{
var localResponse = await Repository.FirstOrDefaultAsync(c => c.Id == response.Id, false);
if (localResponse != null && localResponse.UpdatedAt < response.UpdatedAt)
{
if (localResponse.UpdatedAt >= response.UpdatedAt)
{
if (result.LastUpdate == null || result.LastUpdate < localResponse.UpdatedAt)
result.LastUpdate = localResponse.UpdatedAt;
continue;
}
var deleted = localResponse.Deleted == false && response.Deleted;
localResponse.Version = response.Version;
localResponse.UpdatedAt = response.UpdatedAt;
localResponse.Deleted = response.Deleted;
localResponse.PublicWalkRequestId = response.PublicWalkRequestId;
localResponse.DogWalkerId = response.DogWalkerId;
localResponse.Price = response.Price;
localResponse.Info = response.Info;
localResponse.Answer = response.Answer;
localResponse.Status = response.Status;
localResponse.CancellationReason = response.CancellationReason;
localResponse.Created = response.Created;
if (result.LastUpdate == null || result.LastUpdate < localResponse.UpdatedAt)
result.LastUpdate = localResponse.UpdatedAt;
if (!deleted)
result.Updated.Add(localResponse);
else
result.Deleted.Add(localResponse);
Repository.Update(localResponse);
}
if (localResponse == null && !response.Deleted)
{
localResponse = new PublicWalkResponse()
{
Id = response.Id,
Version = response.Version,
UpdatedAt = response.UpdatedAt,
Deleted = response.Deleted,
PublicWalkRequestId = response.PublicWalkRequestId,
DogWalkerId = response.DogWalkerId,
Price = response.Price,
Info = response.Info,
Answer = response.Answer,
Status = response.Status,
CancellationReason = response.CancellationReason,
Created = response.Created,
};
Repository.Add(localResponse);
if (result.LastUpdate == null || result.LastUpdate < localResponse.UpdatedAt)
result.LastUpdate = localResponse.UpdatedAt;
result.Added.Add(localResponse);
}
}
if (result.HasChanges)
{
try
{
await CommitAsync().ConfigureAwait(false);
}
catch (Exception ex)
{
var err = ex.Message;
}
}
return result;
}
#endregion
}
}