using DiplomovaPrace.Contracts.Customer;
using DiplomovaPrace.Contracts.Customer.Products;
using DiplomovaPrace.Contracts.Customer.Products.ViewModels;
using DiplomovaPrace.DataLayer.Queries.Products;
using DiplomovaPrace.DataLayer.Repositories.Products;
using DiplomovaPrace.Model.Products;
using Havit.Data.Patterns.DataLoaders;
using Havit.Diagnostics.Contracts;
using Havit.Extensions.DependencyInjection.Abstractions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace DiplomovaPrace.Facades.Customer.Products
{
	[Service]
	public class ProductFacade : IProductFacade
	{
		private readonly IProductListQuery productListQuery;
		private readonly IProductRepository productRepository;
		private readonly IDataLoader dataLoader;

		public ProductFacade(
			IProductListQuery productListQuery,
			IProductRepository productRepository,
			IDataLoader dataLoader)
		{
			this.productListQuery = productListQuery;
			this.productRepository = productRepository;
			this.dataLoader = dataLoader;
		}

		public async Task<List<ProductInStockDto>> AreProductsInStockAsync(List<ProductInStockDto> products)
		{
			List<ProductInStockDto> notInStockProducts = new();

			foreach (var product in products)
			{
				Product productDb = await productRepository.GetObjectAsync(product.Id);

				if (productDb is not null && product.Count <= productDb.AvailableQuantity)
				{
					continue; // Reqeusted ammount is in stock
				}

				// Reqeusted ammount is not in stock
				notInStockProducts.Add(new ProductInStockDto {
					Id = productDb.ID,
					Name = productDb.Name,
					Count = product.Count,
					ErrorMessage = $"Zvolené množství: <strong>{product.Count}</strong> kusů produktu: <strong>{productDb.Name}</strong> není skladem. Aktuální počet kusů na skladě je: <strong>{productDb.AvailableQuantity}</strong> kusů. Snižte, prosím, množství produktu na dostupný počet.<br/>"
				});
			}

			return notInStockProducts;
		}

		public async Task<List<ProductVM>> GetAllProductsAsync()
		{
			List<Product> products = await productRepository.GetAllAsync();
			await dataLoader.LoadAllAsync(products, i => i.ProductImages);

			if (products.Count > 0)
			{
				return products.Select(i => new ProductVM
				{
					ID = i.ID,
					Name = i.Name,
					Price = i.Price,
					AvailableQuantity = i.AvailableQuantity,
					ReducedPrice = i.ReducedPrice,
					Description = i.Description,
					ProductCategoryID = i.ProductCategoryID,
					ProductImageIDs = i.ProductImages.Select(i => i.ID).ToList()
				}).OrderBy(i => i.ID).ToList();
			}

			return null;
		}

		public async Task<PagedQueryResult<ProductVM>> GetPagedProductsAsync(PagedQueryRequest<ProductListQueryFilter> pagedQueryRequest)
		{
			Contract.Requires<ArgumentNullException>(pagedQueryRequest is not null, nameof(pagedQueryRequest));

			productListQuery.Filter = pagedQueryRequest.Filter;
			productListQuery.Sorting = pagedQueryRequest.Sorting;

			return await productListQuery.GetPageAsync(pagedQueryRequest.PageIndex, pagedQueryRequest.PageSize);
		}

		public async Task<ProductVM> GetProductAsync(int productId)
		{
			Product product = await productRepository.GetObjectAsync(productId);
			await dataLoader.LoadAsync(product, i => i.ProductCategory);
			await dataLoader.LoadAsync(product, i => i.ProductImages);

			if (product is not null)
			{
				return new ProductVM
				{
					ID = product.ID,
					Name = product.Name,
					Price = product.Price,
					AvailableQuantity = product.AvailableQuantity,
					ReducedPrice = product.ReducedPrice,
					Description = product.Description,
					ProductCategoryID = product.ProductCategoryID,
					ProductCategoryName = product.ProductCategory?.Name ?? string.Empty,
					ProductImageIDs = product.ProductImages.Select(i => i.ID).ToList()
				};
			}

			return null;
		}
	}
}
