using LiteCharms.Features.Extensions; using LiteCharms.Features.Models; using LiteCharms.Features.TechShop.Extensions; using LiteCharms.Features.TechShop.Postgres; using LiteCharms.Features.TechShop.Quotes.Models; namespace LiteCharms.Features.TechShop.Quotes; public class QuoteService(IDbContextFactory contextFactory) { public async ValueTask AssignQuoteToOrderAsync(Guid quoteId, Guid orderId, CancellationToken cancellationToken = default) { try { using var context = await contextFactory.CreateDbContextAsync(cancellationToken); var quote = await context.Quotes.FirstOrDefaultAsync(o => o.Id == quoteId, cancellationToken); if (quote is null) return Result.Fail(new Error($"Quote with id {orderId} not found")); if (!await context.Orders.AnyAsync(q => q.Id == orderId, cancellationToken)) return Result.Fail(new Error($"Order with id {quoteId} not found")); if (quote.OrderId == orderId) return Result.Fail(new Error($"Quote with id {quoteId} is already assigned to order with id {orderId}")); quote.OrderId = orderId; quote.UpdatedAt = DateTime.UtcNow; return await context.SaveChangesAsync(cancellationToken) > 0 ? Result.Ok() : Result.Fail(new Error($"Failed to assign quote with id {quoteId} to order with id {orderId}")); } catch (Exception ex) { return Result.Fail(new Error(ex.Message).CausedBy(ex)); } } public async ValueTask AssignQuoteToShoppingCartAsync(Guid quoteId, Guid shoppingCartId, CancellationToken cancellationToken = default) { try { using var context = await contextFactory.CreateDbContextAsync(cancellationToken); var quote = await context.Quotes.FirstOrDefaultAsync(o => o.Id == quoteId, cancellationToken); if (quote is null) return Result.Fail(new Error($"Quote with id {quoteId} not found")); if (!await context.ShoppingCarts.AnyAsync(q => q.Id == shoppingCartId, cancellationToken)) return Result.Fail(new Error($"Shopping Cart with id {shoppingCartId} not found")); quote.ShoppingCartId = shoppingCartId; quote.UpdatedAt = DateTime.UtcNow; return await context.SaveChangesAsync(cancellationToken) > 0 ? Result.Ok() : Result.Fail(new Error("Failed to assign quote to shopping cart")); } catch (Exception ex) { return Result.Fail(new Error(ex.Message).CausedBy(ex)); } } public async ValueTask> GetCustomerQuotesAsync(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 quotes = await context.Quotes.AsNoTracking() .Where(q => q.CustomerId == customerId).ToArrayAsync(cancellationToken); return quotes?.Length > 0 ? Result.Ok(quotes.Select(q => q.ToModel()).ToArray()) : Result.Fail(new Error($"No quotes found for customer with Id {customerId}.")); } catch (Exception ex) { return Result.Fail(new Error(ex.Message).CausedBy(ex)); } } public async ValueTask> GetQuoteAsync(Guid quoteId, CancellationToken cancellationToken = default) { try { await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); var quote = await context.Quotes.AsNoTracking().FirstOrDefaultAsync(q => q.Id == quoteId, cancellationToken); return quote is not null ? Result.Ok(quote.ToModel()) : Result.Fail(new Error($"Quote with ID {quoteId} not found.")); } catch (Exception ex) { return Result.Fail(new Error(ex.Message).CausedBy(ex)); } } public async ValueTask> GetQuotesAsync(DateRange range, CancellationToken cancellationToken = default) { try { var fromDate = range.From.ToDateTime(TimeOnly.MinValue); var toDate = range.To.ToDateTime(TimeOnly.MaxValue); using var context = await contextFactory.CreateDbContextAsync(cancellationToken); var quotes = await context.Quotes.AsNoTracking() .OrderByDescending(o => o.CreatedAt) .Where(o => o.CreatedAt >= fromDate && o.CreatedAt <= toDate) .Take(range.MaxRecords) .ToArrayAsync(cancellationToken); return quotes?.Length > 0 ? Result.Ok(quotes.Select(o => o.ToModel()).ToArray()) : Result.Fail(new Error($"No quotes found for the specified date range {range.From} - {range.To}.")); } catch (Exception ex) { return Result.Fail(new Error(ex.Message).CausedBy(ex)); } } public async ValueTask UpdateQuoteStatusAsync(Guid quoteId, QuoteStatus status, CancellationToken cancellationToken = default) { try { using var context = await contextFactory.CreateDbContextAsync(cancellationToken); var quote = await context.Quotes.FirstOrDefaultAsync(q => q.Id == quoteId, cancellationToken); if (quote is null) return Result.Fail(new Error("Quote not found.")); quote.Status = status; quote.UpdatedAt = DateTime.UtcNow; return await context.SaveChangesAsync(cancellationToken) > 0 ? Result.Ok() : Result.Fail(new Error("Failed to update quote status.")); } catch (Exception ex) { return Result.Fail(new Error(ex.Message).CausedBy(ex)); } } }