using Application.Abstractions.Data; using Application.Abstractions.Messaging; using Microsoft.EntityFrameworkCore; namespace Application.Features.Api.ChannelTitle.GetMyTitles; internal sealed class Handler(IAppDbContext db) : IQueryHandler { public async Task Handle(Query request, CancellationToken ct) { var stats = await db.DonorChannelStats.AsNoTracking() .Where(s => s.DonorMemberID == request.MemberID && s.CumulativeAmount > 0) .Join(db.Channel.AsNoTracking(), s => s.ChannelID, c => c.ID, (s, c) => new { s.ChannelID, ChannelName = c.Name, ChannelThumb = c.ThumbnailUrl, s.CumulativeAmount, s.DonationCount }) .ToListAsync(ct); if (stats.Count == 0) { return new Response([]); } var channelIds = stats.Select(s => s.ChannelID).ToList(); var titles = await db.ChannelTitle.AsNoTracking() .Where(t => t.IsActive && channelIds.Contains(t.ChannelID)) .OrderBy(t => t.ChannelID).ThenBy(t => t.MinAmount) .ToListAsync(ct); var selections = await db.DonorTitleSelection.AsNoTracking() .Where(s => s.DonorMemberID == request.MemberID && channelIds.Contains(s.ChannelID)) .ToListAsync(ct); var groups = stats.Select(stat => { var selected = selections.FirstOrDefault(s => s.ChannelID == stat.ChannelID); var channelTitles = titles.Where(t => t.ChannelID == stat.ChannelID).ToList(); var items = channelTitles.Select(t => { var acquired = stat.CumulativeAmount >= t.MinAmount; var remaining = Math.Max(0, t.MinAmount - stat.CumulativeAmount); var isSelected = selected?.SelectedTitleID == t.ID; return new MyTitleItem( t.ID, t.Name, t.Description, t.MinAmount, t.Color, t.IconUrl, acquired, isSelected, remaining ); }).ToList(); return new ChannelTitleGroup( stat.ChannelID, stat.ChannelName, stat.ChannelThumb, stat.CumulativeAmount, stat.DonationCount, selected?.SelectedTitleID, items ); }).ToList(); return new Response(groups); } }