using LiteCharms.Features.TechShop.Extensions; using LiteCharms.Features.TechShop.Postgres; using LiteCharms.Features.TechShop.ShoppingCarts.Models; namespace LiteCharms.Features.TechShop.ShoppingCarts; public class ShoppingCartService(IDbContextFactory contextFactory) { public async ValueTask AddItemToShoppingCartAsync(Guid shoppingCartId, Guid productPriceId, int quantity, CancellationToken cancellationToken = default) { try { using var context = await contextFactory.CreateDbContextAsync(cancellationToken); if (!await context.ProductPrices.AnyAsync(c => c.Id == productPriceId, cancellationToken)) return Result.Fail($"Product item could not be found with id {productPriceId}"); var cart = await context.ShoppingCarts.FirstOrDefaultAsync(c => c.Id == shoppingCartId, cancellationToken); if (cart is null) return Result.Fail($"Shopping cart could not be found with id {shoppingCartId}"); if (cart.ShoppingCartItems?.Any(i => i.ProductPriceId == productPriceId) == true) return Result.Fail($"Item already in shopping cart with id {shoppingCartId}"); context.ShoppingCartItems.Add(new Entities.ShoppingCartItem { ShoppingCartId = shoppingCartId, ProductPriceId = productPriceId, Quantity = quantity }); return await context.SaveChangesAsync(cancellationToken) > 0 ? Result.Ok() : Result.Fail($"Failed to add cart item with id {productPriceId}"); } catch (Exception ex) { return Result.Fail(new Error(ex.Message).CausedBy(ex)); } } public async ValueTask AddPackageToShoppingCartAsync(Guid shoppingCartId, Guid packageId, CancellationToken cancellationToken = default) { try { using var context = await contextFactory.CreateDbContextAsync(cancellationToken); if (!await context.Packages.AnyAsync(p => p.Id == packageId, cancellationToken)) return Result.Fail($"Package cold not be found by ID {packageId}"); var shoppingCart = await context.ShoppingCarts.FirstOrDefaultAsync(c => c.Id == shoppingCartId, cancellationToken); if (shoppingCart is null) return Result.Fail($"Shopping cart could not be found by ID {shoppingCartId}"); if (!await context.ShoppingCartPackages.AnyAsync(cp => cp.ShoppingCartId == shoppingCartId, cancellationToken)) return Result.Fail($"Package {packageId} is already in the cart"); var newShoppingCartPackage = context.ShoppingCartPackages.Add(new Entities.ShoppingCartPackage { ShoppingCartId = shoppingCartId, PackageId = packageId }); return await context.SaveChangesAsync(cancellationToken) > 0 ? Result.Ok() : Result.Fail($"Could not add package of id {packageId} to shopping cart {shoppingCartId}"); } catch (Exception ex) { return Result.Fail(new Error(ex.Message).CausedBy(ex)); } } public async ValueTask> CreateShoppingCartAsync(Guid customerId, Guid orderId, CancellationToken cancellationToken = default) { try { using var context = await contextFactory.CreateDbContextAsync(cancellationToken); if (!await context.Customers.AnyAsync(c => c.Id == customerId, cancellationToken)) return Result.Fail($"Customer could not be found with id {customerId}"); var cart = context.ShoppingCarts.Add(new Entities.ShoppingCart { CustomerId = customerId, OrderId = orderId }); return await context.SaveChangesAsync(cancellationToken) > 0 ? Result.Ok(cart.Entity.Id) : Result.Fail($"Failed to create shopping cart for customer id {customerId}"); } catch (Exception ex) { return Result.Fail(new Error(ex.Message).CausedBy(ex)); } } public async ValueTask EmptyShoppingCartAsync(Guid shoppingCartId, CancellationToken cancellationToken = default) { try { using var context = await contextFactory.CreateDbContextAsync(cancellationToken); if (!await context.ShoppingCarts.AnyAsync(c => c.Id == shoppingCartId, cancellationToken)) return Result.Fail($"Shopping could not be found with id {shoppingCartId}"); if (await context.ShoppingCartItems.CountAsync(i => i.ShoppingCartId == shoppingCartId, cancellationToken) == 0) return Result.Ok(); var cartItems = await context.ShoppingCartItems.Where(i => i.ShoppingCartId == shoppingCartId).ToListAsync(cancellationToken); context.RemoveRange(cartItems); return await context.SaveChangesAsync(cancellationToken) > 0 ? Result.Ok() : Result.Fail($"Could not empty cart with id {shoppingCartId}"); } catch (Exception ex) { return Result.Fail(new Error(ex.Message).CausedBy(ex)); } } public async ValueTask> GetCustomerShoppingCartsAsync(Guid customerId, CancellationToken cancellationToken = default) { try { using var context = await contextFactory.CreateDbContextAsync(cancellationToken); if (!await context.Customers.AnyAsync(c => c.Id == customerId, cancellationToken)) return Result.Fail(new Error($"Customer with Id {customerId} does not exist.")); var shoppingCarts = await context.ShoppingCarts.Where(sc => sc.CustomerId == customerId).ToArrayAsync(cancellationToken); return shoppingCarts?.Length > 0 ? Result.Ok(shoppingCarts.Select(c => c.ToModel()).ToArray()) : Result.Fail(new Error($"No shopping carts found for customer with Id {customerId}.")); } catch (Exception ex) { return Result.Fail(new Error(ex.Message).CausedBy(ex)); } } public async ValueTask> GetShoppingCartAsync(Guid shoppingCartId, CancellationToken cancellationToken = default) { try { using var context = await contextFactory.CreateDbContextAsync(cancellationToken); var cart = await context.ShoppingCarts.AsNoTracking().FirstOrDefaultAsync(c => c.Id == shoppingCartId, cancellationToken); return cart is not null ? Result.Ok(cart.ToModel()) : Result.Fail($"Failed to find shopping cart with id {shoppingCartId}"); } catch (Exception ex) { return Result.Fail(new Error(ex.Message).CausedBy(ex)); } } public async ValueTask> GetShoppingCartItemsAsync(Guid shoppingCartId, CancellationToken cancellationToken = default) { try { using var context = await contextFactory.CreateDbContextAsync(cancellationToken); if (!await context.ShoppingCarts.AnyAsync(i => i.Id == shoppingCartId, cancellationToken)) return Result.Fail($"Shopping cart could not be found with id {shoppingCartId}"); var items = await context.ShoppingCartItems.AsNoTracking() .Where(i => i.ShoppingCartId == shoppingCartId).ToArrayAsync(cancellationToken); return items?.Length > 0 ? Result.Ok(items.Select(i => i.ToModel()).ToArray()) : Result.Fail($"Failed to retrieve shopping cart items with id {shoppingCartId}"); } catch (Exception ex) { return Result.Fail(new Error(ex.Message).CausedBy(ex)); } } public async ValueTask> GetShoppingCartPackagesAsync(Guid shoppingCartId, CancellationToken cancellationToken = default) { try { using var context = await contextFactory.CreateDbContextAsync(cancellationToken); if (!await context.ShoppingCarts.AnyAsync(c => c.Id == shoppingCartId, cancellationToken)) return Result.Fail($"Shopping cart could not be found by ID {shoppingCartId}"); var packages = await context.ShoppingCartPackages.AsNoTracking() .OrderByDescending(o => o.CreatedAt) .Where(cp => cp.ShoppingCartId == shoppingCartId) .ToArrayAsync(cancellationToken); return packages?.Length > 0 ? Result.Ok(packages.Select(p => p.ToModel()).ToArray()) : Result.Fail($"Could not find packaged in shopping cart by ID {shoppingCartId}"); } catch (Exception ex) { return Result.Fail(new Error(ex.Message).CausedBy(ex)); } } public async ValueTask RemovePackageFromShoppingCartAsync(Guid shoppingCartId, Guid shoppingCartPackageId, CancellationToken cancellationToken = default) { try { using var context = await contextFactory.CreateDbContextAsync(cancellationToken); if (!await context.ShoppingCarts.AnyAsync(c => c.Id == shoppingCartId, cancellationToken)) return Result.Fail($"Shopping cart could not be found by ID {shoppingCartId}"); if (!await context.ShoppingCartPackages.AnyAsync(p => p.Id == shoppingCartPackageId, cancellationToken)) return Result.Fail($"Shopping cart package {shoppingCartPackageId} is not in the shopping cart {shoppingCartId}"); var shoppingCartPackage = await context.ShoppingCartPackages.FirstOrDefaultAsync(cp => cp.Id == shoppingCartPackageId, cancellationToken); if (shoppingCartPackage is null) return Result.Ok(); context.ShoppingCartPackages.Remove(shoppingCartPackage!); return await context.SaveChangesAsync(cancellationToken) > 0 ? Result.Ok() : Result.Fail($"Could remove package of id {shoppingCartPackageId} from shopping cart {shoppingCartId}"); } catch (Exception ex) { return Result.Fail(new Error(ex.Message).CausedBy(ex)); } } public async ValueTask RemoveShoppingCartItemAsync(Guid shoppingCartId, Guid shoppingCartItemId, CancellationToken cancellationToken = default) { try { using var context = await contextFactory.CreateDbContextAsync(cancellationToken); if (!await context.ProductPrices.AnyAsync(c => c.Id == shoppingCartItemId, cancellationToken)) return Result.Fail($"Product item could not be found with id {shoppingCartItemId}"); var cart = await context.ShoppingCarts.FirstOrDefaultAsync(c => c.Id == shoppingCartId, cancellationToken); if (cart is null) return Result.Fail($"Shopping cart item could not be found with id {shoppingCartId}"); var item = await context.ShoppingCartItems.FirstOrDefaultAsync(i => i.Id == shoppingCartItemId, cancellationToken); if (item is null) return Result.Ok(); context.ShoppingCartItems.Remove(item); return await context.SaveChangesAsync(cancellationToken) > 0 ? Result.Ok() : Result.Fail($"Failed to remove shopping cart item with id {shoppingCartItemId}"); } catch (Exception ex) { return Result.Fail(new Error(ex.Message).CausedBy(ex)); } } public async ValueTask UpdateShoppingCartItemAsync(Guid shoppingCartId, Guid shoppingCartItemId, int quantity, CancellationToken cancellationToken = default) { try { using var context = await contextFactory.CreateDbContextAsync(cancellationToken); if (!await context.ShoppingCarts.AnyAsync(c => c.Id == shoppingCartId, cancellationToken)) return Result.Fail($"Shopping could not be found with id {shoppingCartId}"); var item = await context.ShoppingCartItems.FirstOrDefaultAsync(i => i.ShoppingCartId == shoppingCartId, cancellationToken); if (item is null) return Result.Fail($"Shopping cart item could not be found with id {shoppingCartItemId}"); item.Quantity = quantity; item.UpdatedAt = DateTime.UtcNow; return await context.SaveChangesAsync(cancellationToken) > 0 ? Result.Ok() : Result.Fail($"Failed to update cart item quntity"); } catch (Exception ex) { return Result.Fail(new Error(ex.Message).CausedBy(ex)); } } }