using Microsoft.EntityFrameworkCore; using LittleShop.Data; using LittleShop.Models; using LittleShop.DTOs; using LittleShop.Enums; namespace LittleShop.Services; public class OrderService : IOrderService { private readonly LittleShopContext _context; private readonly ILogger _logger; public OrderService(LittleShopContext context, ILogger logger) { _context = context; _logger = logger; } public async Task> GetAllOrdersAsync() { var orders = await _context.Orders .Include(o => o.OrderItems) .ThenInclude(oi => oi.Product) .Include(o => o.CryptoPayments) .OrderByDescending(o => o.CreatedAt) .ToListAsync(); return orders.Select(MapToDto); } public async Task> GetOrdersByIdentityAsync(string identityReference) { var orders = await _context.Orders .Include(o => o.OrderItems) .ThenInclude(oi => oi.Product) .Include(o => o.CryptoPayments) .Where(o => o.IdentityReference == identityReference) .OrderByDescending(o => o.CreatedAt) .ToListAsync(); return orders.Select(MapToDto); } public async Task GetOrderByIdAsync(Guid id) { var order = await _context.Orders .Include(o => o.OrderItems) .ThenInclude(oi => oi.Product) .Include(o => o.CryptoPayments) .FirstOrDefaultAsync(o => o.Id == id); return order == null ? null : MapToDto(order); } public async Task CreateOrderAsync(CreateOrderDto createOrderDto) { using var transaction = await _context.Database.BeginTransactionAsync(); try { var order = new Order { Id = Guid.NewGuid(), IdentityReference = createOrderDto.IdentityReference, Status = OrderStatus.PendingPayment, TotalAmount = 0, Currency = "GBP", Notes = createOrderDto.Notes, CreatedAt = DateTime.UtcNow }; _context.Orders.Add(order); decimal totalAmount = 0; foreach (var itemDto in createOrderDto.OrderItems) { var product = await _context.Products.FindAsync(itemDto.ProductId); if (product == null || !product.IsActive) { throw new ArgumentException($"Product {itemDto.ProductId} not found or inactive"); } var orderItem = new OrderItem { Id = Guid.NewGuid(), OrderId = order.Id, ProductId = itemDto.ProductId, Quantity = itemDto.Quantity, UnitPrice = product.BasePrice, TotalPrice = product.BasePrice * itemDto.Quantity }; _context.OrderItems.Add(orderItem); totalAmount += orderItem.TotalPrice; } order.TotalAmount = totalAmount; await _context.SaveChangesAsync(); await transaction.CommitAsync(); _logger.LogInformation("Created order {OrderId} for identity {Identity} with total {Total}", order.Id, createOrderDto.IdentityReference, totalAmount); // Reload order with includes var createdOrder = await GetOrderByIdAsync(order.Id); return createdOrder!; } catch { await transaction.RollbackAsync(); throw; } } public async Task UpdateOrderStatusAsync(Guid id, UpdateOrderStatusDto updateOrderStatusDto) { var order = await _context.Orders.FindAsync(id); if (order == null) return false; order.Status = updateOrderStatusDto.Status; if (!string.IsNullOrEmpty(updateOrderStatusDto.TrackingNumber)) { order.TrackingNumber = updateOrderStatusDto.TrackingNumber; } if (!string.IsNullOrEmpty(updateOrderStatusDto.Notes)) { order.Notes = updateOrderStatusDto.Notes; } if (updateOrderStatusDto.Status == OrderStatus.Shipped && order.ShippedAt == null) { order.ShippedAt = DateTime.UtcNow; } await _context.SaveChangesAsync(); _logger.LogInformation("Updated order {OrderId} status to {Status}", id, updateOrderStatusDto.Status); return true; } public async Task CancelOrderAsync(Guid id, string identityReference) { var order = await _context.Orders.FindAsync(id); if (order == null || order.IdentityReference != identityReference) return false; if (order.Status != OrderStatus.PendingPayment) { return false; // Can only cancel pending orders } order.Status = OrderStatus.Cancelled; await _context.SaveChangesAsync(); _logger.LogInformation("Cancelled order {OrderId} by identity {Identity}", id, identityReference); return true; } private static OrderDto MapToDto(Order order) { return new OrderDto { Id = order.Id, IdentityReference = order.IdentityReference, Status = order.Status, TotalAmount = order.TotalAmount, Currency = order.Currency, Notes = order.Notes, TrackingNumber = order.TrackingNumber, CreatedAt = order.CreatedAt, PaidAt = order.PaidAt, ShippedAt = order.ShippedAt, OrderItems = order.OrderItems.Select(oi => new OrderItemDto { Id = oi.Id, ProductId = oi.ProductId, ProductName = oi.Product.Name, Quantity = oi.Quantity, UnitPrice = oi.UnitPrice, TotalPrice = oi.TotalPrice }).ToList(), CryptoPayments = order.CryptoPayments.Select(cp => new CryptoPaymentDto { Id = cp.Id, OrderId = cp.OrderId, Currency = cp.Currency, WalletAddress = cp.WalletAddress, RequiredAmount = cp.RequiredAmount, PaidAmount = cp.PaidAmount, Status = cp.Status, BTCPayInvoiceId = cp.BTCPayInvoiceId, TransactionHash = cp.TransactionHash, CreatedAt = cp.CreatedAt, PaidAt = cp.PaidAt, ExpiresAt = cp.ExpiresAt }).ToList() }; } }