using gehGassi.Core.Interfaces;
using gehGassi.Domain.Walks;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using gehGassi.Domain.Common;
using gehGassi.Domain.Dogs;
using gehGassi.Domain.Ratings;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Microsoft.EntityFrameworkCore;
using gehGassi.Common.Extensions;
namespace gehGassi.Core.Services
{
///
/// Service der die Verwaltung von Ratings ermöglicht
///
public class RatingService : ServiceBase, IRatingService
{
private readonly IRepository _namesRepository;
private readonly IRepository _namesExRepository;
private readonly IRepository _dogsRepository;
private readonly IRepository _appUserRepository;
private readonly IRepository _walksRepository;
///
/// Erstellt eine Instanz
///
/// Instanz eines IUnitOfWork
public RatingService(IUnitOfWork unitOfWork) : base(unitOfWork)
{
_namesRepository = unitOfWork.GetRepository();
_namesExRepository = unitOfWork.GetRepository();
_dogsRepository = unitOfWork.GetRepository();
_appUserRepository = unitOfWork.GetRepository();
_walksRepository = unitOfWork.GetRepository();
}
///
/// Erstellen eines Ratings
///
/// Rating
public Rating Create()
{
var rating = new Rating()
{
Id = Guid.NewGuid().ToString("N"),
Created = DateTimeOffset.UtcNow
};
return rating;
}
///
/// Erstellen eines Ratings
///
/// Id der Rating Quelle
/// Typ der Rating Quelle
/// Id des Ziels des Ratings
/// Typ des Rating Ziels
/// Vergebene Punkte
/// Zusatzinfo zum Rating
/// Rating
public Rating Create(string fromId, AppUserType fromType, string targetId, RatingTarget targetType, decimal points, string info)
{
var rating = new Rating()
{
Id = Guid.NewGuid().ToString("N"),
FromId = fromId,
FromType = fromType,
TargetId = targetId,
TargetType = targetType,
Points = points,
Info = info,
Created = DateTimeOffset.UtcNow
};
return rating;
}
///
/// Gibt eine Liste der Ratings eines Benutzers zurück. Kann Owner und Walker sein
///
/// Id des AppUsers
/// Letztes Update
/// Liste der öffentlichen Anfragen
public async Task> GetForSyncAppAsync(string appUserId, DateTimeOffset? lastUpdate)
{
var query = Repository.Query(c => c.FromId == appUserId);
if (lastUpdate.HasValue)
query = query.Where(c => c.UpdatedAt > lastUpdate);
return await query.ToListAsync().ConfigureAwait(false);
}
///
/// Gibt ein Rating basierend auf Abfragekriterien zurück
///
/// Id der Rating Quelle
/// Typ der Rating Quelle
/// Id des Ziels des Ratings
/// Typ des Rating Ziels
/// Rating wenn gefunden, null sonst
public async Task GetRatingExAsync(string fromId, AppUserType fromType, string targetId, RatingTarget targetType)
{
var rating = await Repository.FirstOrDefaultAsync(c => c.FromId == fromId && c.FromType == fromType && c.TargetId == targetId && c.TargetType == targetType).ConfigureAwait(false);
return rating;
}
///
/// Neu berechnen der Ratings-Statistics für ein Ziel
///
/// Id des Ziels des Ratings
/// Typ des Rating Ziels
/// true wenn erfolgreich, false sonst
public async Task UpdateRatingStatisticsAsync(string targetId, RatingTarget targetType)
{
if (targetType == RatingTarget.Dog || targetType == RatingTarget.Owner || targetType == RatingTarget.Walker)
{
var totalRatings = await Repository.CountAsync(c => c.TargetId == targetId && c.Deleted == false).ConfigureAwait(false);
var totalPoints = (await Repository.FindAsync(c => c.TargetId == targetId && c.Deleted == false).ConfigureAwait(false)).Sum(c => c.Points);
var averageRating = 0m;
if (totalRatings > 0)
averageRating = totalPoints / totalRatings;
if (targetType == RatingTarget.Dog)
{
var dog = await _dogsRepository.FirstOrDefaultAsync(c => c.Id == targetId);
if (dog != null)
{
dog.RatingStatistics.TotalRatings = totalRatings;
dog.RatingStatistics.TotalPoints = totalPoints;
dog.RatingStatistics.AverageRating = averageRating;
dog.UpdatedAt = DateTimeOffset.UtcNow;
return true;
}
}
if (targetType == RatingTarget.Owner)
{
var appUser = await _appUserRepository.FirstOrDefaultAsync(c => c.Id == targetId);
if (appUser != null)
{
appUser.RatingStatistics.TotalRatings = totalRatings;
appUser.RatingStatistics.TotalPoints = totalPoints;
appUser.RatingStatistics.AverageRating = averageRating;
appUser.UpdatedAt = DateTimeOffset.UtcNow;
return true;
}
}
if (targetType == RatingTarget.Walker)
{
var appUser = await _appUserRepository.FirstOrDefaultAsync(c => c.Id == targetId);
if (appUser != null)
{
appUser.RatingStatisticsWalker.TotalRatings = totalRatings;
appUser.RatingStatisticsWalker.TotalPoints = totalPoints;
appUser.RatingStatisticsWalker.AverageRating = averageRating;
appUser.UpdatedAt = DateTimeOffset.UtcNow;
return true;
}
}
}
return false;
}
///
/// Gibt an ob ein Bewerter ein Ziel bereits bewertet hat
///
/// Id der Rating Quelle
/// Typ der Rating Quelle
/// Id des Ziels des Ratings
/// Typ des Rating Ziels
/// true wenn bereits bewertet, false sonst
public async Task HasRatedAsync(string fromId, AppUserType fromType, string targetId, RatingTarget targetType)
{
var rating = await Repository.FirstOrDefaultAsync(c => c.FromId == fromId && c.FromType == fromType && c.TargetId == targetId && c.TargetType == targetType).ConfigureAwait(false);
return rating != null;
}
///
/// Gibt zurück ob ein Bewerter ein Ziel bewerten darf.
/// z.B. ein Walker darf einen hund nur bewerten, wenn mindestens ein Walk mit dem hund stattgefunden hat.
/// Ein Owner darf einen Walker nur bewerten, wenn dieser mindestens einen Walk für den Owen gemacht hat usw.
///
/// Id der Rating Quelle
/// Typ der Rating Quelle
/// Id des Ziels des Ratings
/// Typ des Rating Ziels
/// true wenn eine Bewertung stattfinden darf, false sonst
public async Task CanRateAsync(string fromId, AppUserType fromType, string targetId, RatingTarget targetType)
{
if (targetType == RatingTarget.Dog || targetType == RatingTarget.Owner || targetType == RatingTarget.Walker)
{
//1. Darf ein Walker einen Hund bewerten?
if ((fromType == AppUserType.DogWalker || fromType == AppUserType.Both) && targetType == RatingTarget.Dog)
{
var existingWalk = await _walksRepository.FirstOrDefaultAsync(c => c.DogsJson.Contains(targetId) && c.DogWalkerId == fromId && c.Completed.HasValue).ConfigureAwait(false);
return existingWalk != null;
}
//2. Darf ein Owner einen Walker bewerten?
if ((fromType == AppUserType.DogOwner || fromType == AppUserType.Both) && targetType == RatingTarget.Walker)
{
var existingWalk = await _walksRepository.FirstOrDefaultAsync(c => c.DogOwnerId == fromId && c.DogWalkerId == targetId && c.Completed.HasValue).ConfigureAwait(false);
return existingWalk != null;
}
//2. Darf ein Walker einen Owner bewerten?
if ((fromType == AppUserType.DogWalker || fromType == AppUserType.Both) && targetType == RatingTarget.Owner)
{
var existingWalk = await _walksRepository.FirstOrDefaultAsync(c => c.DogOwnerId == targetId && c.DogWalkerId == fromId && c.Completed.HasValue).ConfigureAwait(false);
return existingWalk != null;
}
}
return false;
}
///
/// Alle Ratings eines AppUsers und für einen AppUser löschen
///
/// Id des AppUsers
/// Anzahl gelöscht
public async Task>> DeleteAllForAppUserAsync(string appUserId)
{
var targetsToUpdate = new List>();
//Zuerst werden alle Ratings gelöscht, die an den AppUser gestellt wurden
var ratings = await Repository.FindAsync(c => c.TargetId == appUserId).ConfigureAwait(false);
foreach (var rating in ratings)
{
Repository.Remove(rating);
}
//Nun alle suchen die von dem Benutzer kommen und diese löschen.
//Achutung: bei den Zielbenutzern muss dann die Statistik aktualisiert werden!
var ratingsFromUser = await Repository.FindAsync(c => c.FromId == appUserId).ConfigureAwait(false);
foreach (var rating in ratingsFromUser)
{
if (rating.TargetType == RatingTarget.Dog || rating.TargetType == RatingTarget.Owner || rating.TargetType == RatingTarget.Walker)
{
targetsToUpdate.Add(new Tuple(rating.TargetId, rating.TargetType));
}
Repository.Remove(rating);
}
return targetsToUpdate;
}
///
/// Gibt eine Liste von Ratings mit Namen aufgelöst zurück
///
/// Id der Rating Quelle
/// Typ der Rating Quelle
/// Id des Ziels des Ratings
/// Typ des Rating Ziels
/// Optional: Liste der sortierung
/// Datensätze auslassen
/// Datensätze nehmen
/// Liste gefudene Walks
public async Task<(int total, List list)> GetWithNamesAsync(string fromId, AppUserType? fromType, string targetId, RatingTarget? targetType, List sortList, int skip, int take)
{
var baseQuery = _namesRepository.QueryStoredProcedure("SELECT * FROM vw_RatingsWithNames", c => c.Deleted == false);
if (!string.IsNullOrWhiteSpace(fromId))
baseQuery = baseQuery.Where(c => c.FromId == fromId);
if (fromType.HasValue)
baseQuery = baseQuery.Where(c => c.FromType == fromType);
if (!string.IsNullOrWhiteSpace(targetId))
baseQuery = baseQuery.Where(c => c.TargetId == targetId);
if (targetType.HasValue)
baseQuery = baseQuery.Where(c => c.TargetType == targetType);
var total = await baseQuery.CountAsync().ConfigureAwait(false);
if (sortList.Any())
{
var isFirst = true;
var orderedBaseQuery = baseQuery.SpecialOrderBy(sortList.First().Property, sortList.First().SortDirection); ;
foreach (var sortOrder in sortList)
{
if (isFirst)
{
isFirst = false;
continue;
}
else
{
orderedBaseQuery = orderedBaseQuery.SpecialThenBy(sortOrder.Property, sortOrder.SortDirection);
}
}
var list = await orderedBaseQuery.Skip(skip).Take(take).ToListAsync().ConfigureAwait(false);
return (total, list);
}
else
{
var list = await baseQuery.OrderByDescending(c => c.UpdatedAt).Skip(skip).Take(take).ToListAsync().ConfigureAwait(false);
return (total, list);
}
}
///
/// Gibt eine Liste von Ratings mit Namen aufgelöst und mit lock und block Info zurück
///
/// Id des Benutzers der abfrägt und für den Blocking geprüft wird
/// Id der Rating Quelle
/// Typ der Rating Quelle
/// Id des Ziels des Ratings
/// Typ des Rating Ziels
/// Optional: Liste der sortierung
/// Datensätze auslassen
/// Datensätze nehmen
/// Liste gefudene Walks
public async Task<(int total, List list)> GetWithNamesExAsync(string blockingAppUserId, string fromId, AppUserType? fromType, string targetId, RatingTarget? targetType, List sortList, int skip, int take)
{
var baseQuery = _namesExRepository.QueryStoredProcedure("SELECT * FROM tv_RatingsWithNamesEx({0})", c => c.Deleted == false, blockingAppUserId);
if (!string.IsNullOrWhiteSpace(fromId))
baseQuery = baseQuery.Where(c => c.FromId == fromId);
if (fromType.HasValue)
baseQuery = baseQuery.Where(c => c.FromType == fromType);
if (!string.IsNullOrWhiteSpace(targetId))
baseQuery = baseQuery.Where(c => c.TargetId == targetId);
if (targetType.HasValue)
baseQuery = baseQuery.Where(c => c.TargetType == targetType);
var total = await baseQuery.CountAsync().ConfigureAwait(false);
if (sortList.Any())
{
var isFirst = true;
var orderedBaseQuery = baseQuery.SpecialOrderBy(sortList.First().Property, sortList.First().SortDirection); ;
foreach (var sortOrder in sortList)
{
if (isFirst)
{
isFirst = false;
continue;
}
else
{
orderedBaseQuery = orderedBaseQuery.SpecialThenBy(sortOrder.Property, sortOrder.SortDirection);
}
}
var list = await orderedBaseQuery.Skip(skip).Take(take).ToListAsync().ConfigureAwait(false);
return (total, list);
}
else
{
var list = await baseQuery.OrderByDescending(c => c.UpdatedAt).Skip(skip).Take(take).ToListAsync().ConfigureAwait(false);
return (total, list);
}
}
///
/// Gibt ein Rating mit Namen aufgelöst zurück
///
/// Id des Ratings
/// Rating oder null, wenn nicht gefunden
public async Task GetWithNamesAsync(string ratingId)
{
return await _namesRepository.FirstOrDefaultAsync(c => c.Id == ratingId).ConfigureAwait(false);
}
}
}