using JSMR.Application.Scanning.Contracts; using JSMR.Application.VoiceWorks.Commands.SetFavorite; using JSMR.Application.VoiceWorks.Ports; using JSMR.Domain.Entities; using Microsoft.EntityFrameworkCore; namespace JSMR.Infrastructure.Data.Repositories.VoiceWorks; public class VoiceWorkWriter(AppDbContext dbContext) : IVoiceWorkWriter { public async Task UpsertAsync(IReadOnlyCollection ingests, CancellationToken cancellationToken) { VoiceWorkUpsertContext upsertContext = await CreateUpsertContextAsync(ingests, cancellationToken); foreach (VoiceWorkIngest ingest in ingests) { Upsert(ingest, upsertContext); } await dbContext.SaveChangesAsync(cancellationToken); return [.. upsertContext.VoiceWorks.Select(x => x.Value.VoiceWorkId)]; } private async Task CreateUpsertContextAsync(IReadOnlyCollection ingests, CancellationToken cancellationToken) { string[] makerIds = [.. ingests.Select(i => i.MakerId).Where(s => !string.IsNullOrWhiteSpace(s)).Distinct()]; string[] productIds = [.. ingests.Select(i => i.ProductId).Distinct()]; string[] tagNames = [.. ingests.SelectMany(i => i.Tags).Distinct()]; string[] creatorNames = [.. ingests.SelectMany(i => i.Creators).Distinct()]; VoiceWorkUpsertContext upsertContext = new( Circles: await dbContext.Circles .Where(c => makerIds.Contains(c.MakerId)) .ToDictionaryAsync(c => c.MakerId, cancellationToken), VoiceWorks: await dbContext.VoiceWorks .Where(v => productIds.Contains(v.ProductId)) .Include(v => v.VoiceWorkCreators) .Include(v => v.VoiceWorkTags) .ToDictionaryAsync(v => v.ProductId, cancellationToken), Tags: await dbContext.Tags .Where(t => tagNames.Contains(t.Name)) .ToDictionaryAsync(t => t.Name, cancellationToken), Creators: await dbContext.Creators .Where(cr => creatorNames.Contains(cr.Name)) .ToDictionaryAsync(cr => cr.Name, cancellationToken) ); return upsertContext; } private void Upsert(VoiceWorkIngest ingest, VoiceWorkUpsertContext upsertContext) { UpsertCircle(ingest, upsertContext); UpsertVoiceWork(ingest, upsertContext); UpsertTags(ingest, upsertContext); UpsertVoiceWorkTags(ingest, upsertContext); UpsertCreators(ingest, upsertContext); UpsertVoiceWorkCreators(ingest, upsertContext); } private void UpsertCircle(VoiceWorkIngest ingest, VoiceWorkUpsertContext upsertContext) { Circle circle = GetOrAddCircle(ingest, upsertContext); circle.Name = ingest.MakerName; } private Circle GetOrAddCircle(VoiceWorkIngest ingest, VoiceWorkUpsertContext upsertContext) { if (!upsertContext.Circles.TryGetValue(ingest.MakerId, out Circle? circle)) { circle = new Circle { MakerId = ingest.MakerId, Name = ingest.MakerName, }; dbContext.Circles.Add(circle); upsertContext.Circles[ingest.MakerId] = circle; } return circle; } private void UpsertVoiceWork(VoiceWorkIngest ingest, VoiceWorkUpsertContext upsertContext) { VoiceWork voiceWork = GetOrAddVoiceWork(ingest, upsertContext); voiceWork.Circle = upsertContext.Circles[ingest.MakerId]; voiceWork.ProductName = ingest.Title; voiceWork.Description = ingest.Description; voiceWork.Downloads = ingest.Downloads; voiceWork.WishlistCount = ingest.WishlistCount; voiceWork.HasTrial = ingest.HasTrial; voiceWork.HasChobit = ingest.HasDLPlay; voiceWork.StarRating = ingest.StarRating; voiceWork.Votes = ingest.Votes; voiceWork.IsValid = true; //var est = i.EstimatedDate?.ToDateTime(new TimeOnly(0, 0)); //if (vw2.ExpectedDate != est) { vw2.ExpectedDate = est; changed = true; } //var sales = i.SalesDate?.ToDateTime(new TimeOnly(0, 0)); //if (vw2.SalesDate != sales) { vw2.SalesDate = sales; changed = true; } } private VoiceWork GetOrAddVoiceWork(VoiceWorkIngest ingest, VoiceWorkUpsertContext upsertContext) { if (!upsertContext.VoiceWorks.TryGetValue(ingest.ProductId, out VoiceWork? voiceWork)) { voiceWork = new VoiceWork { ProductId = ingest.ProductId }; dbContext.VoiceWorks.Add(voiceWork); upsertContext.VoiceWorks[ingest.ProductId] = voiceWork; } return voiceWork; } private void UpsertTags(VoiceWorkIngest ingest, VoiceWorkUpsertContext upsertContext) { foreach (string tagName in ingest.Tags) { GetOrAddTag(tagName, upsertContext); } } private Tag GetOrAddTag(string tagName, VoiceWorkUpsertContext upsertContext) { if (!upsertContext.Tags.TryGetValue(tagName, out Tag? tag)) { tag = new Tag { Name = tagName }; dbContext.Tags.Add(tag); upsertContext.Tags[tagName] = tag; } return tag; } private void UpsertCreators(VoiceWorkIngest ingest, VoiceWorkUpsertContext something) { foreach (string creatorName in ingest.Creators) { GetOrAddCreator(creatorName, something); } } private Creator GetOrAddCreator(string creatorName, VoiceWorkUpsertContext something) { if (!something.Creators.TryGetValue(creatorName, out Creator? creator)) { creator = new Creator { Name = creatorName }; dbContext.Creators.Add(creator); something.Creators[creatorName] = creator; } return creator; } private void UpsertVoiceWorkTags(VoiceWorkIngest ingest, VoiceWorkUpsertContext something) { VoiceWork voiceWork = something.VoiceWorks[ingest.ProductId]; Dictionary existingTagLinks = voiceWork.VoiceWorkTags.ToDictionary(x => x.TagId); int position = 1; foreach (string tagName in ingest.Tags) { Tag tag = something.Tags[tagName]; if (!existingTagLinks.TryGetValue(tag.TagId, out VoiceWorkTag? voiceWorkTag)) { voiceWorkTag = new VoiceWorkTag { VoiceWork = voiceWork, Tag = tag }; dbContext.VoiceWorkTags.Add(voiceWorkTag); } voiceWorkTag.Position = position++; voiceWorkTag.IsValid = true; } } private void UpsertVoiceWorkCreators(VoiceWorkIngest ingest, VoiceWorkUpsertContext something) { VoiceWork voiceWork = something.VoiceWorks[ingest.ProductId]; Dictionary existingCreatorLinks = voiceWork.VoiceWorkCreators.ToDictionary(x => x.CreatorId); int position = 1; foreach (string creatorName in ingest.Creators) { Creator creator = something.Creators[creatorName]; if (!existingCreatorLinks.TryGetValue(creator.CreatorId, out VoiceWorkCreator? voiceWorkCreator)) { voiceWorkCreator = new VoiceWorkCreator { VoiceWork = voiceWork, Creator = creator }; dbContext.VoiceWorkCreators.Add(voiceWorkCreator); } voiceWorkCreator.Position = position++; voiceWorkCreator.IsValid = true; } } public async Task SetFavoriteAsync(SetVoiceWorkFavoriteRequest request, CancellationToken cancellationToken) { VoiceWork voiceWork = await GetVoiceWorkAsync(request.VoiceWorkId, cancellationToken); voiceWork.Favorite = request.IsFavorite; await dbContext.SaveChangesAsync(cancellationToken); return new SetVoiceWorkFavoriteResponse(request.VoiceWorkId, request.IsFavorite); } private async Task GetVoiceWorkAsync(int voiceWorkId, CancellationToken cancellationToken) { return await dbContext.VoiceWorks.FirstOrDefaultAsync(voiceWork => voiceWork.VoiceWorkId == voiceWorkId, cancellationToken) ?? throw new KeyNotFoundException($"Voice Work {voiceWorkId} not found."); } }