using Bp.Sync.Dto.Results;
using MediatR;
using System.Data;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Bp.Sync.BackEnd.Infrastructure.Repositories;
using Bp.Sync.Domain.Elastics.Inventories;
using System.Collections.Generic;
using Bp.Sync.Domain.Elastics.Inventories.Products;
using Nest;

namespace Bp.Sync.BackEnd.Application.Commands.Inventories.ProductInvoices
{
    public class UpdateInvoiceToProductInvoiceHandler : IRequestHandler<UpdateInvoiceToProductInvoiceCommand, BaseResult>
    {
        private readonly IProductInvoiceRepository _ProductInvoiceRepository;
        private readonly IInvoiceRepository _invoiceRepository;
        private readonly IProductRepository _productRepository;
        private readonly IProductInventoryRepository _productInventoryRepository;
        private readonly IUnitRepository _unitRepository;
        private readonly IProductUnitRepository _productUnitRepository;

        public UpdateInvoiceToProductInvoiceHandler(IProductInvoiceRepository ProductInvoiceRepository, IInvoiceRepository invoiceRepository,
            IProductInventoryRepository productInventoryRepository, IProductUnitRepository productUnitRepository,
            IProductRepository productRepository, IUnitRepository unitRepository)
        {
            _ProductInvoiceRepository = ProductInvoiceRepository;
            _invoiceRepository = invoiceRepository;
            _productRepository = productRepository;
            _unitRepository = unitRepository;
            _productInventoryRepository = productInventoryRepository;
            _productUnitRepository = productUnitRepository;
        }
        public async Task<BaseResult> Handle(UpdateInvoiceToProductInvoiceCommand request, CancellationToken cancellationToken)
        {
            var rs = new BaseResult();

            var listUpdated = new List<ProductInvoiceElastic>();

            //lay ra productinvoice k co invoice
            var query = new BoolQueryDescriptor<ProductInvoiceElastic>();
            var notinvoiceDeleteQ = new QueryContainerDescriptor<ProductInvoiceElastic>();
            notinvoiceDeleteQ.Bool(b => b.MustNot(m => m.Exists(e => e.Field(f => f.InvoiceIsDeleted))));
            var notUnitNameQ = new QueryContainerDescriptor<ProductInvoiceElastic>();
            notUnitNameQ.Bool(b => b.MustNot(m => m.Exists(e => e.Field(f => f.UnitName))));
            var notReveneQ = new QueryContainerDescriptor<ProductInvoiceElastic>();
            notReveneQ.Bool(b => b.MustNot(m => m.Exists(e => e.Field(f => f.Revenue))));
            var notTotalProductInventoryCostPriceQ = new QueryContainerDescriptor<ProductInvoiceElastic>();
            notTotalProductInventoryCostPriceQ.Bool(b => b.MustNot(m => m.Exists(e => e.Field(f => f.TotalProductInventoryCostPrice))));
            var notProductUnitQ = new QueryContainerDescriptor<ProductInvoiceElastic>();
            notProductUnitQ.Bool(b => b.MustNot(m => m.Exists(e => e.Field(f => f.ProductUnitId))));

            var idQ = new QueryContainerDescriptor<ProductInvoiceElastic>();
            if (request.Ids.Any()) idQ.Terms(t => t.Field(f => f.Id).Terms(request.Ids));
            if (request.Ids.Any())
                query.Filter(fl => idQ);
            else query.Filter(fl => notinvoiceDeleteQ || notUnitNameQ || notReveneQ || notTotalProductInventoryCostPriceQ || notProductUnitQ);
            var items = await _ProductInvoiceRepository.Search(query, 5000);

            if (items.Items == null || !items.Items.Any()) return new BaseResult { Message = "No data update", Result = Dto.Results.Result.Success };

            var invoiceIds = items.Items.Select(x => x.InvoiceId).Distinct().ToList();
            var invoices = await _invoiceRepository.GetByIds(invoiceIds);
            var productIds = items.Items.Select(x => x.ProductId).Distinct().ToList();
            var products = await _productRepository.GetByIds(productIds);
            var unitIds = items.Items.Select(x => (long)x.UnitId).Distinct().ToList();
            var units = await _unitRepository.GetAll();
            var productUnits = await _productUnitRepository.GetMinUnitByProductIds(productIds);

            var customerIds = invoices.Items.Select(x => x.CustomerId).Distinct().ToList();

            if (invoices.Items != null && invoices.Items.Any())
            {
                foreach (var item in items.Items)
                {
                    var invoice = invoices.Items.FirstOrDefault(x => x.Id == item.InvoiceId);
                    if (invoice != null)
                    {
                        item.InvoiceType = invoice.InvoiceType;
                        item.InvoiceBranchId = invoice.BranchId;
                        item.InvoiceCustomerId = invoice.CustomerId;
                        item.InvoiceCreatedBy = invoice.CreatedBy;
                        item.InvoiceCreatedByUserName = invoice.CreatedByUserName;
                        item.InvoiceCreatedTime = invoice.CreatedTime;
                        item.InvoiceIsDeleted = invoice.IsDeleted;
                        item.InvoiceUpdatedBy = invoice.UpdatedBy;
                        item.InvoiceUpdatedByUserName = invoice.UpdatedByUserName;
                        item.InvoiceUpdatedTime = invoice.UpdatedTime;
                    }
                    var product = products.Items.FirstOrDefault(x => x.Id == item.ProductId);
                    if (product != null)
                    {
                        item.ProductName = product.Name;
                        item.ProductCode = product.Code;
                    }
                    var unit = units.Items.FirstOrDefault(x => x.Id == item.UnitId);
                    if (unit != null)
                    {
                        item.UnitName = unit.Name;
                    }

                    var productunit = productUnits.Items.FirstOrDefault(x => x.ProductId == item.ProductId);
                    if (productunit != null)
                    {
                        item.ProductUnitId = productunit.UnitId;
                        if(item.ProductUnitId.HasValue && item.ProductUnitId.Value > 0)
                        {
                            var unit2 = units.Items.FirstOrDefault(x => x.Id == item.ProductUnitId);
                            if (unit2 != null)
                            {
                                item.ProductUnitName = unit2.Name;
                            }
                        }
                    }
                }

                //thêm productinventory
                //lay ra productinventory
                var inventoryCodes = items.Items.Where(x => !string.IsNullOrEmpty(x.InventoryCode) && !string.IsNullOrWhiteSpace(x.InventoryCode)).Select(x => x.InventoryCode).ToList();
                var branchIds = items.Items.Where(x => x.InvoiceBranchId > 0).Select(x => x.InvoiceBranchId).Distinct().ToList();
                var queryIn = new BoolQueryDescriptor<ProductInventoryElastic>();
                var branchInQ = new QueryContainerDescriptor<ProductInventoryElastic>();
                branchInQ.Terms(e => e.Field(f => f.BranchId).Terms(branchIds));
                var productInQ = new QueryContainerDescriptor<ProductInventoryElastic>();
                productInQ.Terms(e => e.Field(f => f.ProductId).Terms(productIds));
                var inventoryCodeQ = new QueryContainerDescriptor<ProductInventoryElastic>();
                if (inventoryCodes.Any())
                    inventoryCodeQ.Terms(e => e.Field(f => f.InventoryCode.Suffix("keyword")).Terms(inventoryCodes));
                queryIn.Filter(f => branchInQ && inventoryCodeQ && productInQ);
                var productInventories = await _productInventoryRepository.Search(queryIn);

                foreach (var item in items.Items)
                {
                    var productinventory = productInventories.Items.FirstOrDefault(x => x.InventoryCode == item.InventoryCode && x.BranchId == item.InvoiceBranchId && x.ProductId == item.ProductId);
                    if (productinventory != null)
                    {
                        item.ProductInventoryCostPrice = productinventory.CostPrice;
                    }
                    listUpdated.Add(item);
                }
            }

            if (listUpdated.Any())
            {
                var rs2 = await _ProductInvoiceRepository.AddManyAsync(listUpdated);
                if (rs2.Result == Dto.Results.Result.Success)
                {
                    rs.Message = $"{listUpdated.Count} - updated";
                    rs.Result = Dto.Results.Result.Success;
                }
                else
                {
                    rs.Message = $"can not insert db";
                    rs.Result = Dto.Results.Result.Success;
                }
            }
            return rs;
        }
    }
}
