using CMSMicroservice.Protobuf.Protos.DiscountProduct; using CMSMicroservice.Protobuf.Protos.DiscountCategory; using Google.Protobuf.WellKnownTypes; namespace FrontOffice.Main.Utilities; // ── DTOs ── public record DiscountProductCard( long Id, string Title, string ShortInfo, long Price, int MaxDiscountPercent, string ImageUrl, string ThumbnailUrl, int RemainingCount, int ViewCount, bool IsActive, DateTime Created); public record DiscountProductDetail( long Id, string Title, string ShortInfo, string FullInfo, long Price, int MaxDiscountPercent, string ImageUrl, string ThumbnailUrl, int RemainingCount, int ViewCount, int SortOrder, bool IsActive, List Categories, List Images, DateTime Created); public record DiscountCategoryInfo(long Id, string Name, string Title); public record DiscountProductImageDto( long Id, string ImageUrl, string ThumbnailUrl, string? Title, string? AltText, int SortOrder); public record DiscountCategoryNode( long Id, string Name, string Title, string? Description, string? ImagePath, long? ParentCategoryId, int SortOrder, bool IsActive, int ProductCount, List Children); public record DiscountProductListResult( List Products, int TotalCount, int TotalPages, int CurrentPage); // ── Service ── public class DiscountProductService { private readonly DiscountProductContract.DiscountProductContractClient _productClient; private readonly DiscountCategoryContract.DiscountCategoryContractClient _categoryClient; public DiscountProductService( DiscountProductContract.DiscountProductContractClient productClient, DiscountCategoryContract.DiscountCategoryContractClient categoryClient) { _productClient = productClient; _categoryClient = categoryClient; } public async Task GetProductsAsync( int page = 1, int pageSize = 12, string? search = null, long? categoryId = null, bool? inStock = null, string? sortBy = null) { try { var request = new GetDiscountProductsRequest { PageNumber = page, PageSize = pageSize, IsActive = true // فقط محصولات فعال }; if (!string.IsNullOrWhiteSpace(search)) request.SearchQuery = search; if (categoryId.HasValue) request.CategoryId = categoryId.Value; if (inStock.HasValue) request.InStock = inStock.Value; if (!string.IsNullOrWhiteSpace(sortBy)) request.SortBy = sortBy; var response = await _productClient.GetDiscountProductsAsync(request); var products = response.Models.Select(m => new DiscountProductCard( Id: m.Id, Title: m.Title ?? string.Empty, ShortInfo: m.ShortInfomation ?? string.Empty, Price: m.Price, MaxDiscountPercent: m.MaxDiscountPercent, ImageUrl: BuildUrl(m.ImagePath), ThumbnailUrl: BuildUrl(m.ThumbnailPath), RemainingCount: m.RemainingCount, ViewCount: m.ViewCount, IsActive: m.IsActive, Created: m.Created?.ToDateTime() ?? DateTime.MinValue )).ToList(); var totalCount = (int)(response.MetaData?.TotalCount ?? 0); var totalPages = (int)(response.MetaData?.TotalPage ?? 0); return new DiscountProductListResult(products, totalCount, totalPages, page); } catch { return new DiscountProductListResult(new(), 0, 0, page); } } public Task GetTopSellingAsync(int count = 6) => GetProductsAsync(page: 1, pageSize: count, sortBy: "SaleCount desc"); public async Task GetByIdAsync(long productId) { try { var response = await _productClient.GetDiscountProductByIdAsync( new GetDiscountProductByIdRequest { ProductId = productId }); var categories = response.Categories .Select(c => new DiscountCategoryInfo(c.Id, c.Name ?? "", c.Title ?? "")) .ToList(); // Load images var imagesResponse = await _productClient.GetDiscountProductImagesAsync( new GetDiscountProductImagesRequest { DiscountProductId = productId, OnlyActive = true }); var images = imagesResponse.Images .OrderBy(i => i.SortOrder) .Select(i => new DiscountProductImageDto( Id: i.Id, ImageUrl: BuildUrl(i.ImagePath), ThumbnailUrl: BuildUrl(i.ThumbnailPath), Title: i.Title, AltText: i.AltText, SortOrder: i.SortOrder)) .ToList(); return new DiscountProductDetail( Id: response.Id, Title: response.Title ?? string.Empty, ShortInfo: response.ShortInfomation ?? string.Empty, FullInfo: response.FullInformation ?? string.Empty, Price: response.Price, MaxDiscountPercent: response.MaxDiscountPercent, ImageUrl: BuildUrl(response.ImagePath), ThumbnailUrl: BuildUrl(response.ThumbnailPath), RemainingCount: response.RemainingCount, ViewCount: response.ViewCount, SortOrder: response.SortOrder, IsActive: response.IsActive, Categories: categories, Images: images, Created: response.Created?.ToDateTime() ?? DateTime.MinValue); } catch { return null; } } public async Task> GetCategoriesAsync(bool onlyActive = true) { try { var request = new GetDiscountCategoriesRequest(); if (onlyActive) request.IsActive = true; var response = await _categoryClient.GetDiscountCategoriesAsync(request); return response.Categories.Select(MapCategory).ToList(); } catch { return new(); } } private static DiscountCategoryNode MapCategory(DiscountCategoryDto dto) => new( Id: dto.Id, Name: dto.Name ?? "", Title: dto.Title ?? "", Description: dto.Description, ImagePath: dto.ImagePath, ParentCategoryId: dto.ParentCategoryId, SortOrder: dto.SortOrder, IsActive: dto.IsActive, ProductCount: dto.ProductCount, Children: dto.Children.Select(MapCategory).ToList()); private static string BuildUrl(string? path) => path ?? string.Empty; }