using CartWise.Application.Interfaces; using CartWise.Application.Results; using CartWise.Domain.Entities; using CartWise.Domain.Enums; using Microsoft.EntityFrameworkCore; namespace CartWise.Application.Services; public class ShoppingListService : IShoppingListService { private readonly IApplicationDbContext _db; private readonly TimeProvider _timeProvider; public ShoppingListService(IApplicationDbContext db, TimeProvider timeProvider) { _db = db; _timeProvider = timeProvider; } public async Task GetActiveListAsync(Guid householdId, CancellationToken cancellationToken = default) { return await _db.ShoppingLists .Include(l => l.Items) .AsNoTracking() .Where(l => l.HouseholdId == householdId && l.Status == ShoppingListStatus.Active) .FirstOrDefaultAsync(cancellationToken); } public async Task> CreateListAsync(Guid householdId, string name, string createdByUserId, CancellationToken cancellationToken = default) { var hasActiveList = await _db.ShoppingLists .AsNoTracking() .AnyAsync(l => l.HouseholdId == householdId && l.Status == ShoppingListStatus.Active, cancellationToken); if (hasActiveList) { return Result.Failure("This household already has an active shopping list."); } ShoppingList list; try { list = new ShoppingList(householdId, name, createdByUserId, _timeProvider.GetUtcNow().UtcDateTime); } catch (ArgumentException ex) { return Result.Failure(ex.Message); } _db.ShoppingLists.Add(list); await _db.SaveChangesAsync(cancellationToken); return Result.Success(list); } public async Task> AddItemAsync( Guid shoppingListId, string displayName, decimal? quantity, string? unit, string addedByUserId, CancellationToken cancellationToken = default) { var list = await _db.ShoppingLists .Include(l => l.Items) .FirstOrDefaultAsync(l => l.ShoppingListId == shoppingListId, cancellationToken); if (list is null) { return Result.Failure("Shopping list not found."); } ShoppingListItem item; try { item = list.AddItem(displayName, quantity, unit, addedByUserId, _timeProvider.GetUtcNow().UtcDateTime); } catch (Exception ex) when (ex is ArgumentException or InvalidOperationException) { return Result.Failure(ex.Message); } // EF Core can't reliably infer Added state for a new child discovered only via a mutated // navigation collection on an already-tracked parent, since ShoppingListItemId is a // client-generated Guid rather than a store-generated key. Mark it explicitly. _db.ShoppingListItems.Add(item); await _db.SaveChangesAsync(cancellationToken); return Result.Success(item); } }