using CMSMicroservice.Protobuf.Protos.Products;
using Grpc.Core;
using MediatR;
using CMSMicroservice.Application.ProductsCQ.Commands.CreateNewProducts;
using CMSMicroservice.Application.ProductsCQ.Commands.UpdateProducts;
using CMSMicroservice.Application.ProductsCQ.Commands.DeleteProducts;
using CMSMicroservice.Application.ProductsCQ.Commands.AddProductImage;
using CMSMicroservice.Application.ProductsCQ.Commands.RemoveProductImage;
using CMSMicroservice.Application.ProductsCQ.Queries.GetCustomerProducts;
using CMSMicroservice.Application.ProductsCQ.Queries.GetCustomerProductsByFilter;
using CMSMicroservice.Application.ProductsCQ.Queries.GetProductGallery;
using CMSMicroservice.Application.Common.Interfaces;
using Mapster;
using Microsoft.EntityFrameworkCore;
using AppModels = CMSMicroservice.Application.Common.Models;
using System.Collections.Generic;
using System.Linq;

namespace CMSMicroservice.WebApi.Services;

public class ProductsService : ProductsContract.ProductsContractBase
{
    private readonly ISender _sender;
    private readonly IApplicationDbContext _context;

    public ProductsService(ISender sender, IApplicationDbContext context)
    {
        _sender = sender;
        _context = context;
    }
    public override async Task<CreateNewProductsResponse> CreateNewProducts(CreateNewProductsRequest request, ServerCallContext context)
    {
        var command = new CreateNewProductsCommand
        {
            Title = request.Title,
            Description = request.Description,
            ShortInfomation = request.ShortInfomation,
            FullInformation = request.FullInformation,
            Price = request.Price,
            Discount = request.Discount,
            Rate = request.Rate,
            ImagePath = request.ImagePath,
            ThumbnailPath = request.ThumbnailPath,
            SaleCount = request.SaleCount,
            ViewCount = request.ViewCount,
            RemainingCount = request.RemainingCount,
            CategoryIds = request.CategoryIds?.ToList() ?? new List<long>(),
            ImageFileBytes = request.ImageFile?.File?.ToByteArray(),
            ImageFileMime = request.ImageFile?.Mime,
            ImageFileName = request.ImageFile?.FileName,
            ThumbnailFileBytes = request.ThumbnailFile?.File?.ToByteArray(),
            ThumbnailFileMime = request.ThumbnailFile?.Mime,
            ThumbnailFileName = request.ThumbnailFile?.FileName
        };
        
        var result = await _sender.Send(command, context.CancellationToken);
        return new CreateNewProductsResponse { Id = result.Id };
    }
    
    public override async Task<Google.Protobuf.WellKnownTypes.Empty> UpdateProducts(UpdateProductsRequest request, ServerCallContext context)
    {
        var command = new UpdateProductsCommand
        {
            Id = request.Id,
            Title = request.Title,
            Description = request.Description,
            ShortInfomation = request.ShortInfomation,
            FullInformation = request.FullInformation,
            Price = request.Price,
            Discount = request.Discount,
            Rate = request.Rate,
            ImagePath = request.ImagePath,
            ThumbnailPath = request.ThumbnailPath,
            SaleCount = request.SaleCount,
            ViewCount = request.ViewCount,
            RemainingCount = request.RemainingCount,
            CategoryIds = request.CategoryIds?.ToList() ?? new List<long>(),
            ImageFileBytes = request.ImageFile?.File?.ToByteArray(),
            ImageFileMime = request.ImageFile?.Mime,
            ImageFileName = request.ImageFile?.FileName,
            ThumbnailFileBytes = request.ThumbnailFile?.File?.ToByteArray(),
            ThumbnailFileMime = request.ThumbnailFile?.Mime,
            ThumbnailFileName = request.ThumbnailFile?.FileName
        };
        
        await _sender.Send(command, context.CancellationToken);
        return new Google.Protobuf.WellKnownTypes.Empty();
    }
    
    public override async Task<Google.Protobuf.WellKnownTypes.Empty> DeleteProducts(DeleteProductsRequest request, ServerCallContext context)
    {
        var command = new DeleteProductsCommand { Id = request.Id };
        await _sender.Send(command, context.CancellationToken);
        return new Google.Protobuf.WellKnownTypes.Empty();
    }
    
    public override async Task<GetProductsResponse> GetProducts(GetProductsRequest request, ServerCallContext context)
    {
        var query = new GetCustomerProductsQuery { Id = request.Id };
        var result = await _sender.Send(query, context.CancellationToken);

        var response = new GetProductsResponse
        {
            Id = result.Id,
            Title = result.Title,
            Description = result.Description,
            ShortInfomation = result.ShortInfomation,
            FullInformation = result.FullInformation,
            Price = result.Price,
            Discount = result.Discount,
            Rate = result.Rate,
            ImagePath = result.ImagePath,
            ThumbnailPath = result.ThumbnailPath,
            SaleCount = result.SaleCount,
            ViewCount = result.ViewCount,
            RemainingCount = result.RemainingCount
        };

        if (result.Gallery != null)
        {
            foreach (var item in result.Gallery)
            {
                response.Gallery.Add(new ProductGalleryItem
                {
                    ProductGalleryId = item.ProductGalleryId,
                    ProductImageId = item.ProductImageId,
                    Title = item.Title,
                    ImagePath = item.ImagePath,
                    ImageThumbnailPath = item.ImageThumbnailPath
                });
            }
        }

        if (result.Categories != null)
        {
            foreach (var cat in result.Categories)
            {
                var categoryPath = new ProductCategoryPath
                {
                    CategoryId = cat.CategoryId,
                    Title = cat.Title
                };
                if (cat.Path != null)
                {
                    foreach (var node in cat.Path)
                    {
                        categoryPath.Path.Add(new CategoryNode
                        {
                            Id = node.Id,
                            Title = node.Title,
                            ParentId = node.ParentId
                        });
                    }
                }
                response.Categories.Add(categoryPath);
            }
        }

        return response;
    }
    
    public override async Task<GetAllProductsByFilterResponse> GetAllProductsByFilter(GetAllProductsByFilterRequest request, ServerCallContext context)
    {
        // Map to GetCustomerProductsByFilter query (public products API)
        var query = new GetCustomerProductsByFilterQuery
        {
            Id = request.Filter?.Id,
            Title = request.Filter?.Title ?? string.Empty,
            Description = request.Filter?.Description ?? string.Empty,
            ShortInfomation = request.Filter?.ShortInfomation ?? string.Empty,
            FullInformation = request.Filter?.FullInformation ?? string.Empty,
            Price = request.Filter?.Price,
            Discount = request.Filter?.Discount,
            Rate = request.Filter?.Rate,
            SaleCount = request.Filter?.SaleCount,
            ViewCount = request.Filter?.ViewCount,
            RemainingCount = request.Filter?.RemainingCount,
            InStock = request.Filter?.InStock,
            CategoryIds = request.Filter?.CategoryId.HasValue == true 
                ? new List<long> { request.Filter.CategoryId.Value } 
                : new List<long>(),
            IsActive = request.Filter?.IsActive,
            SortBy = request.SortBy ?? string.Empty,
            PaginationState = request.PaginationState != null 
                ? new AppModels.PaginationState 
                { 
                    PageNumber = request.PaginationState.PageNumber, 
                    PageSize = request.PaginationState.PageSize 
                } 
                : new AppModels.PaginationState { PageNumber = 1, PageSize = 20 }
        };
        
        var result = await _sender.Send(query, context.CancellationToken);
        
        return new GetAllProductsByFilterResponse
        {
            MetaData = new CMSMicroservice.Protobuf.Protos.MetaData
            {
                CurrentPage = result.MetaData.CurrentPage,
                TotalPage = result.MetaData.TotalPage,
                PageSize = result.MetaData.PageSize,
                TotalCount = result.MetaData.TotalCount,
                HasPrevious = result.MetaData.HasPrevious,
                HasNext = result.MetaData.HasNext
            },
            Models = { result.Models.Select(m => new GetAllProductsByFilterResponseModel
            {
                Id = m.Id,
                Title = m.Title,
                Description = m.Description,
                ShortInfomation = m.ShortInfomation,
                FullInformation = m.FullInformation,
                Price = m.Price,
                Discount = m.Discount,
                Rate = m.Rate,
                ImagePath = m.ImagePath,
                ThumbnailPath = m.ThumbnailPath,
                SaleCount = m.SaleCount,
                ViewCount = m.ViewCount,
                RemainingCount = m.RemainingCount,
                CategoryIds = { m.Categories?.Select(c => c.CategoryId) ?? Enumerable.Empty<long>() },
                IsActive = m.IsActive
            }) }
        };
    }
    
    public override async Task<BulkUpdateProductPricesResponse> BulkUpdateProductPrices(BulkUpdateProductPricesRequest request, ServerCallContext context)
    {
        var response = new BulkUpdateProductPricesResponse { Total = request.Products.Count };
        
        foreach (var item in request.Products)
        {
            try
            {
                var product = await _context.Products.FindAsync(new object[] { item.ProductId }, context.CancellationToken);
                if (product == null)
                {
                    response.Failed++;
                    response.Errors.Add(new BulkOperationError { ProductId = item.ProductId, ErrorMessage = "محصول یافت نشد" });
                    continue;
                }
                
                product.Price = item.NewPrice;
                if (item.NewDiscount != null) product.Discount = item.NewDiscount.Value;
                if (item.NewClubDiscountPercent != null) product.ClubDiscountPercent = item.NewClubDiscountPercent.Value;
                
                response.Succeeded++;
            }
            catch (Exception ex)
            {
                response.Failed++;
                response.Errors.Add(new BulkOperationError { ProductId = item.ProductId, ErrorMessage = ex.Message });
            }
        }
        
        await _context.SaveChangesAsync(context.CancellationToken);
        return response;
    }
    
    public override async Task<BulkUpdateProductStockResponse> BulkUpdateProductStock(BulkUpdateProductStockRequest request, ServerCallContext context)
    {
        var response = new BulkUpdateProductStockResponse { Total = request.Products.Count };
        
        foreach (var item in request.Products)
        {
            try
            {
                var product = await _context.Products.FindAsync(new object[] { item.ProductId }, context.CancellationToken);
                if (product == null)
                {
                    response.Failed++;
                    response.Errors.Add(new BulkOperationError { ProductId = item.ProductId, ErrorMessage = "محصول یافت نشد" });
                    continue;
                }
                
                product.RemainingCount = request.UpdateType switch
                {
                    StockUpdateType.Set => item.Quantity,
                    StockUpdateType.Add => product.RemainingCount + item.Quantity,
                    StockUpdateType.Subtract => Math.Max(0, product.RemainingCount - item.Quantity),
                    _ => item.Quantity
                };
                
                response.Succeeded++;
            }
            catch (Exception ex)
            {
                response.Failed++;
                response.Errors.Add(new BulkOperationError { ProductId = item.ProductId, ErrorMessage = ex.Message });
            }
        }
        
        await _context.SaveChangesAsync(context.CancellationToken);
        return response;
    }
    
    public override async Task<GetLowStockProductsResponse> GetLowStockProducts(GetLowStockProductsRequest request, ServerCallContext context)
    {
        var threshold = request.Threshold > 0 ? request.Threshold : 10;
        var pageIndex = request.PageIndex > 0 ? request.PageIndex : 1;
        var pageSize = request.PageSize > 0 ? request.PageSize : 20;
        
        var query = _context.Products
            .Where(p => !p.IsDeleted && p.RemainingCount <= threshold);
        
        if (request.IsClubExclusive != null)
            query = query.Where(p => p.IsClubExclusive == request.IsClubExclusive.Value);
        
        var totalCount = await query.CountAsync(context.CancellationToken);
        
        var products = await query
            .OrderBy(p => p.RemainingCount)
            .Skip((pageIndex - 1) * pageSize)
            .Take(pageSize)
            .Select(p => new LowStockProduct
            {
                Id = p.Id,
                Title = p.Title,
                RemainingCount = p.RemainingCount,
                Price = p.Price,
                IsClubExclusive = p.IsClubExclusive
            })
            .ToListAsync(context.CancellationToken);
        
        return new GetLowStockProductsResponse
        {
            MetaData = new CMSMicroservice.Protobuf.Protos.MetaData
            {
                CurrentPage = pageIndex,
                PageSize = pageSize,
                TotalCount = totalCount,
                TotalPage = (int)Math.Ceiling(totalCount / (double)pageSize),
                HasPrevious = pageIndex > 1,
                HasNext = pageIndex * pageSize < totalCount
            },
            Products = { products }
        };
    }
    
    public override async Task<ToggleProductStatusResponse> ToggleProductStatus(ToggleProductStatusRequest request, ServerCallContext context)
    {
        var response = new ToggleProductStatusResponse { Total = request.ProductIds.Count };
        
        foreach (var productId in request.ProductIds)
        {
            try
            {
                var product = await _context.Products.FindAsync(new object[] { productId }, context.CancellationToken);
                if (product == null)
                {
                    response.Failed++;
                    response.Errors.Add(new BulkOperationError { ProductId = productId, ErrorMessage = "محصول یافت نشد" });
                    continue;
                }
                
                product.IsDeleted = !request.Enable;
                if (request.Enable && request.DefaultStock > 0 && product.RemainingCount == 0)
                    product.RemainingCount = request.DefaultStock;
                
                response.Succeeded++;
            }
            catch (Exception ex)
            {
                response.Failed++;
                response.Errors.Add(new BulkOperationError { ProductId = productId, ErrorMessage = ex.Message });
            }
        }
        
        await _context.SaveChangesAsync(context.CancellationToken);
        return response;
    }
    
    // ============= Category-Product DragDrop Methods =============
    
    public override async Task<GetProductsForCategoryResponse> GetProductsForCategory(GetProductsForCategoryRequest request, ServerCallContext context)
    {
        var assignedProductIds = await _context.ProductCategories
            .Where(pc => pc.CategoryId == request.CategoryId && !pc.IsDeleted)
            .Select(pc => pc.ProductId)
            .ToListAsync(context.CancellationToken);
        
        var allProducts = await _context.Products
            .Where(p => !p.IsDeleted)
            .OrderBy(p => p.Title)
            .Select(p => new CategoryProductItem
            {
                Id = p.Id,
                Title = p.Title,
                Selected = assignedProductIds.Contains(p.Id)
            })
            .ToListAsync(context.CancellationToken);
        
        return new GetProductsForCategoryResponse { Items = { allProducts } };
    }
    
    public override async Task<GetCategoriesResponse> GetCategories(GetCategoriesRequest request, ServerCallContext context)
    {
        var assignedCategoryIds = await _context.ProductCategories
            .Where(pc => pc.ProductId == request.ProductId && !pc.IsDeleted)
            .Select(pc => pc.CategoryId)
            .ToListAsync(context.CancellationToken);
        
        var allCategories = await _context.Categories
            .Where(c => !c.IsDeleted && c.IsActive)
            .OrderBy(c => c.SortOrder)
            .Select(c => new CategoryItem
            {
                Id = c.Id,
                Title = c.Title,
                Selected = assignedCategoryIds.Contains(c.Id)
            })
            .ToListAsync(context.CancellationToken);
        
        return new GetCategoriesResponse { Items = { allCategories } };
    }
    
    public override async Task<Google.Protobuf.WellKnownTypes.Empty> UpdateProductCategories(UpdateProductCategoriesRequest request, ServerCallContext context)
    {
        var existingLinks = await _context.ProductCategories
            .Where(pc => pc.ProductId == request.ProductId)
            .ToListAsync(context.CancellationToken);
        
        // حذف لینک‌های قبلی
        foreach (var link in existingLinks)
            link.IsDeleted = true;
        
        // ایجاد لینک‌های جدید
        foreach (var categoryId in request.CategoryIds)
        {
            var existing = existingLinks.FirstOrDefault(l => l.CategoryId == categoryId);
            if (existing != null)
            {
                existing.IsDeleted = false;
            }
            else
            {
                _context.ProductCategories.Add(new Domain.Entities.ProductCategory
                {
                    ProductId = request.ProductId,
                    CategoryId = categoryId
                });
            }
        }
        
        await _context.SaveChangesAsync(context.CancellationToken);
        return new Google.Protobuf.WellKnownTypes.Empty();
    }
    
    public override async Task<Google.Protobuf.WellKnownTypes.Empty> UpdateCategoryProducts(UpdateCategoryProductsRequest request, ServerCallContext context)
    {
        var existingLinks = await _context.ProductCategories
            .Where(pc => pc.CategoryId == request.CategoryId)
            .ToListAsync(context.CancellationToken);
        
        // حذف لینک‌های قبلی
        foreach (var link in existingLinks)
            link.IsDeleted = true;
        
        // ایجاد لینک‌های جدید
        foreach (var productId in request.ProductIds)
        {
            var existing = existingLinks.FirstOrDefault(l => l.ProductId == productId);
            if (existing != null)
            {
                existing.IsDeleted = false;
            }
            else
            {
                _context.ProductCategories.Add(new Domain.Entities.ProductCategory
                {
                    ProductId = productId,
                    CategoryId = request.CategoryId
                });
            }
        }
        
        await _context.SaveChangesAsync(context.CancellationToken);
        return new Google.Protobuf.WellKnownTypes.Empty();
    }
    
    // ============= Customer-specific Methods =============
    
    public override async Task<GetProductsResponse> GetCustomerProducts(GetProductsRequest request, ServerCallContext context)
    {
        var query = new GetCustomerProductsQuery { Id = request.Id };
        var result = await _sender.Send(query, context.CancellationToken);

        var response = new GetProductsResponse
        {
            Id = result.Id,
            Title = result.Title,
            Description = result.Description,
            ShortInfomation = result.ShortInfomation,
            FullInformation = result.FullInformation,
            Price = result.Price,
            Discount = result.Discount,
            Rate = result.Rate,
            ImagePath = result.ImagePath,
            ThumbnailPath = result.ThumbnailPath,
            SaleCount = result.SaleCount,
            ViewCount = result.ViewCount,
            RemainingCount = result.RemainingCount
        };

        // Add gallery items
        if (result.Gallery != null)
        {
            foreach (var item in result.Gallery)
            {
                response.Gallery.Add(new ProductGalleryItem
                {
                    ProductGalleryId = item.ProductGalleryId,
                    ProductImageId = item.ProductImageId,
                    Title = item.Title,
                    ImagePath = item.ImagePath,
                    ImageThumbnailPath = item.ImageThumbnailPath
                });
            }
        }

        // Add categories
        if (result.Categories != null)
        {
            foreach (var cat in result.Categories)
            {
                var categoryPath = new ProductCategoryPath
                {
                    CategoryId = cat.CategoryId,
                    Title = cat.Title
                };

                if (cat.Path != null)
                {
                    foreach (var node in cat.Path)
                    {
                        categoryPath.Path.Add(new CategoryNode
                        {
                            Id = node.Id,
                            Title = node.Title,
                            ParentId = node.ParentId
                        });
                    }
                }

                response.Categories.Add(categoryPath);
            }
        }

        return response;
    }
    
    public override async Task<GetCustomerProductsByFilterResponse> GetCustomerProductsByFilter(GetAllProductsByFilterRequest request, ServerCallContext context)
    {
        var query = new GetCustomerProductsByFilterQuery
        {
            PaginationState = request.PaginationState?.Adapt<AppModels.PaginationState>(),
            SortBy = request.SortBy,
            Id = request.Filter?.Id,
            Title = request.Filter?.Title,
            Description = request.Filter?.Description,
            ShortInfomation = request.Filter?.ShortInfomation,
            FullInformation = request.Filter?.FullInformation,
            Price = request.Filter?.Price,
            Discount = request.Filter?.Discount,
            Rate = request.Filter?.Rate,
            ImagePath = request.Filter?.ImagePath,
            ThumbnailPath = request.Filter?.ThumbnailPath,
            SaleCount = request.Filter?.SaleCount,
            ViewCount = request.Filter?.ViewCount,
            RemainingCount = request.Filter?.RemainingCount,
            InStock = request.Filter?.InStock,
            CategoryIds = request.Filter?.CategoryId != null ? new List<long> { request.Filter.CategoryId.Value } : null
        };

        var result = await _sender.Send(query, context.CancellationToken);

        var response = new GetCustomerProductsByFilterResponse
        {
            MetaData = new CMSMicroservice.Protobuf.Protos.MetaData
            {
                CurrentPage = result.MetaData.CurrentPage,
                TotalPage = result.MetaData.TotalPage,
                PageSize = result.MetaData.PageSize,
                TotalCount = result.MetaData.TotalCount,
                HasPrevious = result.MetaData.HasPrevious,
                HasNext = result.MetaData.HasNext
            }
        };

        foreach (var model in result.Models)
        {
            var productModel = new GetCustomerProductsByFilterResponseModel
            {
                Id = model.Id,
                Title = model.Title,
                Description = model.Description,
                ShortInfomation = model.ShortInfomation,
                FullInformation = model.FullInformation,
                Price = model.Price,
                Discount = model.Discount,
                Rate = model.Rate,
                ImagePath = model.ImagePath,
                ThumbnailPath = model.ThumbnailPath,
                SaleCount = model.SaleCount,
                ViewCount = model.ViewCount,
                RemainingCount = model.RemainingCount
            };

            if (model.Categories != null)
            {
                foreach (var cat in model.Categories)
                {
                    var categoryPath = new ProductCategoryPath
                    {
                        CategoryId = cat.CategoryId,
                        Title = cat.Title
                    };

                    if (cat.Path != null)
                    {
                        foreach (var node in cat.Path)
                        {
                            categoryPath.Path.Add(new CategoryNode
                            {
                                Id = node.Id,
                                Title = node.Title,
                                ParentId = node.ParentId
                            });
                        }
                    }

                    productModel.Categories.Add(categoryPath);
                }
            }

            response.Models.Add(productModel);
        }

        return response;
    }
    
    // ============= Product Image Management =============
    
    public override async Task<AddProductImageResponse> AddProductImage(AddProductImageRequest request, ServerCallContext context)
    {
        var command = new AddProductImageCommand
        {
            ProductId = request.ProductId,
            Title = request.Title,
            ImageFileBytes = request.ImageFile?.File?.ToByteArray(),
            ImageFileMime = request.ImageFile?.Mime,
            ImageFileName = request.ImageFile?.FileName
        };
        
        var result = await _sender.Send(command, context.CancellationToken);
        
        return new AddProductImageResponse
        {
            ProductGalleryId = result.ProductGalleryId,
            ProductImageId = result.ProductImageId,
            Title = result.Title,
            ImagePath = result.ImagePath,
            ImageThumbnailPath = result.ImageThumbnailPath
        };
    }
    
    public override async Task<Google.Protobuf.WellKnownTypes.Empty> RemoveProductImage(RemoveProductImageRequest request, ServerCallContext context)
    {
        var command = new RemoveProductImageCommand { ProductGalleryId = request.ProductGalleryId };
        await _sender.Send(command, context.CancellationToken);
        return new Google.Protobuf.WellKnownTypes.Empty();
    }
    
    public override async Task<GetProductGalleryResponse> GetProductGallery(GetProductGalleryRequest request, ServerCallContext context)
    {
        var query = new GetProductGalleryQuery { ProductId = request.ProductId };
        var result = await _sender.Send(query, context.CancellationToken);
        
        var response = new GetProductGalleryResponse();
        foreach (var item in result.Items)
        {
            response.Items.Add(new ProductGalleryItem
            {
                ProductGalleryId = item.ProductGalleryId,
                ProductImageId = item.ProductImageId,
                Title = item.Title,
                ImagePath = item.ImagePath,
                ImageThumbnailPath = item.ImageThumbnailPath
            });
        }
        return response;
    }
}
