using Dto; using Dto.Tiny; using Microsoft.Extensions.Logging; using Model; using Model.Repository; using Shared; using EFMappers; using Entities; using Entities2Dto; using Microsoft.EntityFrameworkCore; namespace Model2Entities; public partial class DbDataManager { public class UserRepository : IUserRepository { private readonly DbDataManager _dataManager; private readonly ILogger _logger; public UserRepository(DbDataManager dbDataManager, ILogger logger) { _dataManager = dbDataManager; _logger = logger; } public async Task> GetItems(int index, int count, string? orderingProperty = null, bool descending = false) => await GetUsers(index, count, this.ToEnum(orderingProperty), descending); public async Task> GetUsers(int index, int count, AthleteOrderCriteria? orderingProperty = null, bool descending = false) { _logger.LogInformation($"GetItems with index {index} and count {count}", index, count); _logger.LogInformation($"GetItems with orderingProperty {orderingProperty} and descending {descending}", orderingProperty, descending); var users = _dataManager.DbContext.AthletesSet.IncludeStandardProperties().GetItemsWithFilterAndOrdering(b => true, index, count, orderingProperty != AthleteOrderCriteria.None ? orderingProperty : null, descending).ToModels(); _logger.LogInformation($"Retrieved {users.Count()} users"); return await Task.FromResult(users); } public async Task?> GetUsersTiny(int index, int count, AthleteOrderCriteria? criteria, bool descending = false) { var users = _dataManager.DbContext.AthletesSet.GetItemsWithFilterAndOrdering(b => true, index, count, criteria != AthleteOrderCriteria.None ? criteria : null, descending); _logger.LogInformation($"Retrieved {users.Count()} users"); return await Task.FromResult(users.ToTinyDtos()); } public async Task GetItemById(int id) { _logger.LogInformation($"GetItemById with id {id}", id); var userEntity = await _dataManager.DbContext.AthletesSet.IncludeStandardProperties() .SingleOrDefaultAsync(a => a.Id == id); var user = userEntity != null ? userEntity.ToModel() : null; if (user != null) _logger.LogInformation($"Retrieved user with ID {id}"); else _logger.LogWarning($"No user found with ID {id}"); return await Task.FromResult(user); } public async Task UpdateUser(int old, UserTinyDto user) { _logger.LogInformation($"UpdateUser with id {old}", old); var originalEntity = _dataManager.DbContext.AthletesSet.Find(old); if (originalEntity == null) { _logger.LogWarning($"No user found with ID {old}"); return await Task.FromResult(null); } var originalEntry = _dataManager.DbContext.Entry(originalEntity); var values = typeof(AthleteEntity).GetProperties().Where(ppty => ppty.Name != "IdAthlete") .ToDictionary(ppty => ppty.Name, ppty => ppty.GetValue(user.ToEntity())); originalEntry.CurrentValues.SetValues(values); _dataManager.DbContext.AthletesSet.Attach(originalEntity); _dataManager.DbContext.Entry(originalEntity).State = EntityState.Modified; _dataManager.DbContext.SaveChanges(); var updatedUser = originalEntity.ToTinyDto(); if (updatedUser != null) _logger.LogInformation($"Updated user with ID {old}"); else _logger.LogWarning($"No user found with ID {old}"); return await Task.FromResult(updatedUser); } public async Task GetUserById(int id) { _logger.LogInformation($"GetTinyItemById with id {id}", id); var userEntity = await _dataManager.DbContext.AthletesSet.IncludeStandardProperties().Include(a => a.Followers).Include(a => a.Followings) .SingleOrDefaultAsync(a => a.Id == id); var user = userEntity != null ? userEntity.ToResponseDto() : null; if (user != null) _logger.LogInformation($"Retrieved user with ID {id}"); else _logger.LogWarning($"No user found with ID {id}"); return user; } public async Task UpdateItem(int oldItem, User newItem) { _logger.LogInformation($"UpdateItem with id {oldItem}", oldItem); var originalEntity = _dataManager.DbContext.AthletesSet.Find(oldItem); if (originalEntity == null) { _logger.LogWarning($"No user found with ID {oldItem}"); return await Task.FromResult(null); } var originalEntry = _dataManager.DbContext.Entry(originalEntity); var values = typeof(AthleteEntity).GetProperties().Where(ppty => ppty.Name != "IdAthlete") .ToDictionary(ppty => ppty.Name, ppty => ppty.GetValue(newItem.ToEntity())); originalEntry.CurrentValues.SetValues(values); _dataManager.DbContext.AthletesSet.Attach(originalEntity); _dataManager.DbContext.Entry(originalEntity).State = EntityState.Modified; _dataManager.DbContext.SaveChanges(); var updatedUser = originalEntity.ToModel(); if (updatedUser != null) _logger.LogInformation($"Updated user with ID {oldItem}"); else _logger.LogWarning($"No user found with ID {oldItem}"); return await Task.FromResult(updatedUser); } public async Task AddItem(User item) { _logger.LogInformation("Adding new user"); var addedUser = (await _dataManager.DbContext.AddItem(item.ToEntity()))?.ToModel(); if (addedUser != null) _logger.LogInformation($"Added user with ID {addedUser.Id}"); else _logger.LogError("Failed to add user"); return await Task.FromResult(addedUser); } public async Task DeleteItem(int item) { _logger.LogInformation($"DeleteItem with id {item}", item); var deleted = await _dataManager.DbContext.DeleteItem(item); if (deleted) _logger.LogInformation($"Deleted user with ID {item}"); else _logger.LogWarning($"No user found with ID {item}"); return await Task.FromResult(deleted); } public async Task GetNbItems() { _logger.LogInformation("GetNbItems"); var nbItems = await _dataManager.DbContext.AthletesSet.CountAsync(); _logger.LogInformation($"Retrieved {nbItems} users"); return await Task.FromResult(nbItems); } public async Task> GetAllAthletes(int index, int count, AthleteOrderCriteria? criteria, bool descending = false) { _logger.LogInformation($"GetAllAthletes with index {index} and count {count}", index, count); _logger.LogInformation($"GetAllAthletes with criteria {criteria} and descending {descending}", criteria, descending); var athletes = _dataManager.DbContext.AthletesSet.IncludeStandardProperties() .GetItemsWithFilterAndOrdering(a => a.IsCoach == false, index, count, criteria != AthleteOrderCriteria.None ? criteria : null, descending).ToModels(); _logger.LogInformation($"Retrieved {athletes.Count()} athletes"); return await Task.FromResult(athletes); } public async Task> GetAllCoaches(int index, int count, AthleteOrderCriteria? criteria, bool descending = false) { _logger.LogInformation($"GetAllCoaches with index {index} and count {count}", index, count); _logger.LogInformation($"GetAllCoaches with criteria {criteria} and descending {descending}", criteria, descending); var coaches = _dataManager.DbContext.AthletesSet.IncludeStandardProperties() .GetItemsWithFilterAndOrdering(a => a.IsCoach, index, count, criteria != AthleteOrderCriteria.None ? criteria : null, descending).ToModels(); _logger.LogInformation($"Retrieved {coaches.Count()} coaches"); return await Task.FromResult(coaches); } public async Task AddFollowing(int fromUser, int toUser) { _logger.LogInformation($"Attempting to add following: User {fromUser} adding Following {toUser}"); var userEntity = _dataManager.DbContext.AthletesSet .Include(a => a.Followings) .FirstOrDefault(a => a.Id == fromUser); if (userEntity == null) { _logger.LogWarning($"User not found: User {fromUser}"); throw new FriendShipException("User with id " + fromUser + " not found"); } if (userEntity.Followings.Any(f => f.FollowingId == toUser)) { _logger.LogInformation($"Following already exists: User {fromUser} and Following {toUser}"); throw new FriendShipException("Following already exists"); } await _dataManager.DbContext.SaveChangesAsync(); userEntity.Followings.Add(new FriendshipEntity { FollowingId = toUser, FollowerId = fromUser, StartDate = DateTime.Now }); await _dataManager.DbContext.SaveChangesAsync(); _logger.LogInformation($"Successfully following: from User {fromUser} to Following {toUser}"); return true; } public async Task RemoveFollowing(int fromUser, int toUser){ _logger.LogInformation($"Attempting to remove following: User {fromUser} removing Following {toUser}"); var userEntity = _dataManager.DbContext.AthletesSet .Include(a => a.Followings) .FirstOrDefault(a => a.Id == fromUser); if (userEntity == null) { _logger.LogWarning($"User not found: User {fromUser}"); throw new FriendShipException("User with id " + fromUser + " not found"); } var friendship = userEntity.Followings.FirstOrDefault(f => f.FollowingId == toUser); if (friendship == null) { _logger.LogInformation($"Following not found: User {fromUser} and Following {toUser}"); throw new FriendShipException("Following not found"); } await _dataManager.DbContext.SaveChangesAsync(); userEntity.Followings.Remove(friendship); await _dataManager.DbContext.SaveChangesAsync(); _logger.LogInformation($"Successfully removed following: from User {fromUser} to Following {toUser}"); return await Task.FromResult(true); } public async Task?> GetFriends(int userId, int index, int count, AthleteOrderCriteria? criteria, bool descending = false) { try { _logger.LogInformation($"GetFriends called with index {index}, count {count}, criteria {criteria}, and descending {descending}"); var athlete = await _dataManager.DbContext.AthletesSet .Include(a => a.Followers).ThenInclude(f => f.Follower) .Include(a => a.Followings).ThenInclude(f => f.Following) .FirstOrDefaultAsync(a => a.Id == userId); // Use async version for better performance if (athlete == null) { _logger.LogError("Athlete with id {id} not found", userId); throw new ModelNotFoundException($"Athlete with id {userId} not found"); } var friendsDtos = athlete.Followings .Where(f => athlete.Followers.Any(ff => ff.FollowerId == f.FollowingId)) .Select(f => f.Following).GetItemsWithFilterAndOrdering(a => true, index, count, criteria != AthleteOrderCriteria.None ? criteria : null, descending).ToTinyDtos(); var userTinyDtos = friendsDtos.ToArray(); _logger.LogInformation($"Retrieved {userTinyDtos.Count()} friends for user {userId}"); return userTinyDtos; } catch (Exception ex) { _logger.LogError(ex.Message, ex.InnerException, ex.StackTrace); return null; } } public async Task GetNbFriends(int userId) { try { _logger.LogInformation($"GetNbFriends called for user {userId}"); var athlete = await _dataManager.DbContext.AthletesSet .Include(a => a.Followers).ThenInclude(f => f.Follower) .Include(a => a.Followings).ThenInclude(f => f.Following) .FirstOrDefaultAsync(a => a.Id == userId); if (athlete == null) { _logger.LogError("Athlete with id {id} not found", userId); throw new ModelNotFoundException($"Athlete with id {userId} not found"); } // Count the number of mutual friendships var nbFriends = athlete.Followings .Count(f => athlete.Followers.Any(ff => ff.FollowerId == f.FollowingId)); _logger.LogInformation($"User {userId} has {nbFriends} friends"); return nbFriends; } catch (Exception ex) { _logger.LogError(ex, "An error occurred while counting friends for user {UserId}", userId); throw; // Consider handling the exception outside of this method or logging it accordingly. } } } }