using Fitness_core_BE.Exceptions; using Fitness_core_BE.Interfaces; using Fitness_core_BE.Models; using Microsoft.EntityFrameworkCore; using System.Linq.Expressions; namespace Fitness_core_BE.Repos { public class FeedRepository : IFeedRepository { // FeedPost/FeedLike are central-bucket (poster identity spans gyms); Staff and // CustomerSubscriptions used to scope a feed to one client live in the tenant DB. private readonly CentralDbContext _central; private readonly TenantDbContext _dbContext; public FeedRepository(CentralDbContext central, TenantDbContext dbContext) { _central = central; _dbContext = dbContext; } public async Task GetFeedPostByIdAsync(Guid creatorId, Guid postId) { FeedPost feedPost = await _central.FeedPosts .Where(p => p.FKUserId == creatorId && p.FeedPostId == postId) .FirstOrDefaultAsync(); if (feedPost == null) throw new NotFoundException("Post : " + postId + " not found"); return feedPost; } public async Task> GetFeedListAsync(int skip, int take) { return await _central.FeedPosts .Include(fp => fp.User) .Where(fp => fp.IsActive) .OrderByDescending(fp => fp.CreatedAt) .Skip(skip) .Take(take) .ToListAsync(); } public async Task IsLikedAsync(Guid postId, Guid userId) { return await _central.FeedLikes .AnyAsync(x => x.FKFeedPostId == postId && x.FKUserId == userId); } public async Task AddLikeAsync(Guid postId, Guid userId) { _central.FeedLikes.Add(new FeedLike { FKFeedPostId = postId, FKUserId = userId }); } public async Task RemoveLikeAsync(Guid postId, Guid userId) { var like = await _central.FeedLikes .FirstOrDefaultAsync(x => x.FKFeedPostId == postId && x.FKUserId == userId); if (like != null) { _central.FeedLikes.Remove(like); } } public async Task GetLikeCountAsync(Guid postId) { return await _central.FeedLikes .CountAsync(x => x.FKFeedPostId == postId); } public async Task CreateFeedAsync(FeedPost feedPost, CancellationToken ct = default) { await _central.FeedPosts.AddAsync(feedPost, ct); return true; } public async Task> GetFeedByClientAsync(Guid clientId) { // Client (central) and Staff (tenant) can't be joined in one query anymore -- // resolve the set of poster user ids (owner + staff) first, then filter posts. var posterUserIds = new List(); var clientOwnerUserId = await _central.Client .Where(client => client.ClientId == clientId) .Select(client => (Guid?)client.FKUserId) .FirstOrDefaultAsync(); if (clientOwnerUserId != null) posterUserIds.Add(clientOwnerUserId.Value); var staffUserIds = await _dbContext.Staff .Where(staff => staff.FKClientId == clientId) .Select(staff => staff.FKUserId) .ToListAsync(); posterUserIds.AddRange(staffUserIds); return await _central.FeedPosts .Include(fp => fp.User) .Where(fp => fp.IsActive && posterUserIds.Contains(fp.FKUserId)) .OrderByDescending(fp => fp.CreatedAt) .ToListAsync(); } public async Task<(Guid? ClientId, List Posts)> GetFeedForCustomerAsync( Guid customerUserId, int skip, int take, CancellationToken ct = default) { var cusId = await _central.Customer .Where(customer => customer.FKUserId == customerUserId) .Select(customer => (Guid?)customer.CusId) .FirstOrDefaultAsync(ct); var clientId = Guid.Empty; if (cusId != null) { clientId = await _dbContext.CustomerSubscriptions .Where(subscription => subscription.FKCustomerId == cusId.Value && subscription.IsActive) .Select(subscription => subscription.CustomerPlan.FKClientId) .FirstOrDefaultAsync(ct); } // No active gym: there's no single gym feed to scope to, so fall back to the // platform-wide feed (every active post) instead of showing nothing. if (clientId == Guid.Empty) { var globalPosts = await _central.FeedPosts .AsNoTracking() .Include(post => post.User) .Where(post => post.IsActive) .OrderByDescending(post => post.CreatedAt) .Skip(skip) .Take(take) .ToListAsync(ct); return (null, globalPosts); } // Poster set for this customer's gym = owner + staff (mirrors GetFeedByClientAsync, // the client-owner's own view) -- otherwise a staff member's post would show up on // the gym's own dashboard but never reach that gym's customers. var posterUserIds = new List(); var clientOwnerUserId = await _central.Client .Where(client => client.ClientId == clientId) .Select(client => (Guid?)client.FKUserId) .FirstOrDefaultAsync(ct); if (clientOwnerUserId != null) posterUserIds.Add(clientOwnerUserId.Value); var staffUserIds = await _dbContext.Staff .Where(staff => staff.FKClientId == clientId) .Select(staff => staff.FKUserId) .ToListAsync(ct); posterUserIds.AddRange(staffUserIds); // Show the gym's own posts alongside this customer's own posts, so a customer // who creates a post can see it here too, not just their gym's feed. var posts = await _central.FeedPosts .AsNoTracking() .Include(post => post.User) .Where(post => post.IsActive && (posterUserIds.Contains(post.FKUserId) || post.FKUserId == customerUserId)) .OrderByDescending(post => post.CreatedAt) .Skip(skip) .Take(take) .ToListAsync(ct); return (clientId, posts); } public async Task> GetActiveCustomerIdsByPosterUserIdAsync(Guid posterUserId, CancellationToken ct = default) { var clientId = await _central.Client .Where(client => client.FKUserId == posterUserId) .Select(client => (Guid?)client.ClientId) .FirstOrDefaultAsync(ct); if (clientId == null || clientId == Guid.Empty) return new List(); return await _dbContext.CustomerSubscriptions .Where(subscription => subscription.IsActive && subscription.CustomerPlan.FKClientId == clientId) .Select(subscription => subscription.FKCustomerId) .Distinct() .ToListAsync(ct); } public async Task DeletePostPostIdAsync(Guid creatorId, Guid postId) { FeedPost post = await _central.FeedPosts .Where(p => p.FeedPostId == postId && p.FKUserId == creatorId) .FirstOrDefaultAsync(); if (post == null) throw new NotFoundException("Post : " + postId + "not found"); post.IsActive = false; try { _central.Update(post); return new { process = "success" }; } catch (Exception ex) { throw new Exception(ex.Message, ex); } } } }