using Microsoft.EntityFrameworkCore; using LittleShop.Data; using LittleShop.Models; using LittleShop.DTOs; namespace LittleShop.Services; public class ProductService : IProductService { private readonly LittleShopContext _context; private readonly IWebHostEnvironment _environment; public ProductService(LittleShopContext context, IWebHostEnvironment environment) { _context = context; _environment = environment; } public async Task> GetAllProductsAsync() { return await _context.Products .Include(p => p.Category) .Include(p => p.Photos) .Where(p => p.IsActive) .Select(p => new ProductDto { Id = p.Id, Name = p.Name, Description = p.Description, Price = p.Price, Weight = p.Weight, WeightUnit = p.WeightUnit, StockQuantity = p.StockQuantity, CategoryId = p.CategoryId, CategoryName = p.Category.Name, CreatedAt = p.CreatedAt, UpdatedAt = p.UpdatedAt, IsActive = p.IsActive, Photos = p.Photos.OrderBy(ph => ph.SortOrder).Select(ph => new ProductPhotoDto { Id = ph.Id, FileName = ph.FileName, FilePath = ph.FilePath, AltText = ph.AltText, SortOrder = ph.SortOrder }).ToList() }) .ToListAsync(); } public async Task> GetProductsByCategoryAsync(Guid categoryId) { return await _context.Products .Include(p => p.Category) .Include(p => p.Photos) .Where(p => p.IsActive && p.CategoryId == categoryId) .Select(p => new ProductDto { Id = p.Id, Name = p.Name, Description = p.Description, Price = p.Price, Weight = p.Weight, WeightUnit = p.WeightUnit, StockQuantity = p.StockQuantity, CategoryId = p.CategoryId, CategoryName = p.Category.Name, CreatedAt = p.CreatedAt, UpdatedAt = p.UpdatedAt, IsActive = p.IsActive, Photos = p.Photos.OrderBy(ph => ph.SortOrder).Select(ph => new ProductPhotoDto { Id = ph.Id, FileName = ph.FileName, FilePath = ph.FilePath, AltText = ph.AltText, SortOrder = ph.SortOrder }).ToList() }) .ToListAsync(); } public async Task GetProductByIdAsync(Guid id) { var product = await _context.Products .Include(p => p.Category) .Include(p => p.Photos) .FirstOrDefaultAsync(p => p.Id == id); if (product == null) return null; return new ProductDto { Id = product.Id, Name = product.Name, Description = product.Description, Price = product.Price, Weight = product.Weight, WeightUnit = product.WeightUnit, StockQuantity = product.StockQuantity, CategoryId = product.CategoryId, CategoryName = product.Category.Name, CreatedAt = product.CreatedAt, UpdatedAt = product.UpdatedAt, IsActive = product.IsActive, Photos = product.Photos.OrderBy(ph => ph.SortOrder).Select(ph => new ProductPhotoDto { Id = ph.Id, FileName = ph.FileName, FilePath = ph.FilePath, AltText = ph.AltText, SortOrder = ph.SortOrder }).ToList() }; } public async Task CreateProductAsync(CreateProductDto createProductDto) { var product = new Product { Id = Guid.NewGuid(), Name = createProductDto.Name, Description = string.IsNullOrEmpty(createProductDto.Description) ? " " : createProductDto.Description, Price = createProductDto.Price, Weight = createProductDto.Weight, WeightUnit = createProductDto.WeightUnit, StockQuantity = createProductDto.StockQuantity, CategoryId = createProductDto.CategoryId, CreatedAt = DateTime.UtcNow, UpdatedAt = DateTime.UtcNow, IsActive = true }; _context.Products.Add(product); await _context.SaveChangesAsync(); var category = await _context.Categories.FindAsync(createProductDto.CategoryId); return new ProductDto { Id = product.Id, Name = product.Name, Description = product.Description, Price = product.Price, Weight = product.Weight, WeightUnit = product.WeightUnit, CategoryId = product.CategoryId, CategoryName = category?.Name ?? "", CreatedAt = product.CreatedAt, UpdatedAt = product.UpdatedAt, IsActive = product.IsActive, Photos = new List() }; } public async Task UpdateProductAsync(Guid id, UpdateProductDto updateProductDto) { var product = await _context.Products.FindAsync(id); if (product == null) return false; if (!string.IsNullOrEmpty(updateProductDto.Name)) product.Name = updateProductDto.Name; if (!string.IsNullOrEmpty(updateProductDto.Description)) product.Description = updateProductDto.Description; if (updateProductDto.Price.HasValue) product.Price = updateProductDto.Price.Value; if (updateProductDto.Weight.HasValue) product.Weight = updateProductDto.Weight.Value; if (updateProductDto.WeightUnit.HasValue) product.WeightUnit = updateProductDto.WeightUnit.Value; if (updateProductDto.StockQuantity.HasValue) product.StockQuantity = updateProductDto.StockQuantity.Value; if (updateProductDto.CategoryId.HasValue) product.CategoryId = updateProductDto.CategoryId.Value; if (updateProductDto.IsActive.HasValue) product.IsActive = updateProductDto.IsActive.Value; product.UpdatedAt = DateTime.UtcNow; await _context.SaveChangesAsync(); return true; } public async Task DeleteProductAsync(Guid id) { var product = await _context.Products.FindAsync(id); if (product == null) return false; product.IsActive = false; await _context.SaveChangesAsync(); return true; } public async Task AddProductPhotoAsync(Guid productId, IFormFile file, string? altText = null) { var product = await _context.Products.FindAsync(productId); if (product == null) return false; var uploadsPath = Path.Combine(_environment.WebRootPath ?? "wwwroot", "uploads", "products"); Directory.CreateDirectory(uploadsPath); var fileName = $"{Guid.NewGuid()}_{file.FileName}"; var filePath = Path.Combine(uploadsPath, fileName); using (var stream = new FileStream(filePath, FileMode.Create)) { await file.CopyToAsync(stream); } var maxSortOrder = await _context.ProductPhotos .Where(pp => pp.ProductId == productId) .Select(pp => (int?)pp.SortOrder) .MaxAsync() ?? 0; var productPhoto = new ProductPhoto { Id = Guid.NewGuid(), ProductId = productId, FileName = fileName, FilePath = $"/uploads/products/{fileName}", AltText = altText, SortOrder = maxSortOrder + 1, CreatedAt = DateTime.UtcNow }; _context.ProductPhotos.Add(productPhoto); await _context.SaveChangesAsync(); return true; } public async Task RemoveProductPhotoAsync(Guid productId, Guid photoId) { var photo = await _context.ProductPhotos .FirstOrDefaultAsync(pp => pp.Id == photoId && pp.ProductId == productId); if (photo == null) return false; var physicalPath = Path.Combine(_environment.WebRootPath, photo.FilePath.TrimStart('/')); if (File.Exists(physicalPath)) { File.Delete(physicalPath); } _context.ProductPhotos.Remove(photo); await _context.SaveChangesAsync(); return true; } public async Task AddProductPhotoAsync(CreateProductPhotoDto photoDto) { var product = await _context.Products.FindAsync(photoDto.ProductId); if (product == null) return null; var maxSortOrder = await _context.ProductPhotos .Where(pp => pp.ProductId == photoDto.ProductId) .Select(pp => pp.SortOrder) .DefaultIfEmpty(0) .MaxAsync(); var productPhoto = new ProductPhoto { Id = Guid.NewGuid(), ProductId = photoDto.ProductId, FileName = Path.GetFileName(photoDto.PhotoUrl), FilePath = photoDto.PhotoUrl, AltText = photoDto.AltText, SortOrder = photoDto.DisplayOrder > 0 ? photoDto.DisplayOrder : maxSortOrder + 1, CreatedAt = DateTime.UtcNow }; _context.ProductPhotos.Add(productPhoto); await _context.SaveChangesAsync(); return new ProductPhotoDto { Id = productPhoto.Id, FileName = productPhoto.FileName, FilePath = productPhoto.FilePath, AltText = productPhoto.AltText, SortOrder = productPhoto.SortOrder }; } public async Task> SearchProductsAsync(string searchTerm) { var query = _context.Products .Include(p => p.Category) .Include(p => p.Photos) .Where(p => p.IsActive); if (!string.IsNullOrWhiteSpace(searchTerm)) { searchTerm = searchTerm.ToLower(); query = query.Where(p => p.Name.ToLower().Contains(searchTerm) || p.Description.ToLower().Contains(searchTerm)); } return await query.Select(p => new ProductDto { Id = p.Id, Name = p.Name, Description = p.Description, Price = p.Price, Weight = p.Weight, WeightUnit = p.WeightUnit, StockQuantity = p.StockQuantity, CategoryId = p.CategoryId, CategoryName = p.Category.Name, CreatedAt = p.CreatedAt, UpdatedAt = p.UpdatedAt, IsActive = p.IsActive, Photos = p.Photos.OrderBy(ph => ph.SortOrder).Select(ph => new ProductPhotoDto { Id = ph.Id, FileName = ph.FileName, FilePath = ph.FilePath, AltText = ph.AltText, SortOrder = ph.SortOrder }).ToList() }).ToListAsync(); } }