using Bp.Sync.BackEnd.Infrastructure.Repositories;
using Bp.Sync.BackEnd.Infrastructure.Repositories.FcBusinesses;
using Bp.Sync.Domain.Elastics.Inventories;
using Bp.Sync.Domain.Elastics.Inventories.Bills;
using Bp.Sync.Domain.Elastics.Inventories.Configurations;
using Bp.Sync.Domain.Elastics.Inventories.Products;
using Bp.Sync.Domain.Entities.Inventories.Products;
using Bp.Sync.Dto.Results;
using CommonLibrary;
using Dapper;
using EasyNetQ;
using MediatR;
using Microsoft.Extensions.Configuration;
using Nest;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Vina.Search.Infrastructure.Mappings;

namespace Bp.Sync.BackEnd.Application.Commands.Inventories.Products
{
    public class UpdateProductInventoryHandler : IRequestHandler<UpdateProductInventoryCommand, BaseResult>
    {
        private readonly IDbConnection _dbConnection;
        private readonly IProductInventoryRepository _ProductInventoryRepository;
        private readonly IProductBillRepository _producBillRepository;
        private readonly IBatchRepository _batchRepository;
        private readonly IBus _bus;
        private readonly IContractDiscountLevelRepository _contractDiscountLevelRepository;
        private readonly IInboundBillRepository _inboundBillRepository;

        public UpdateProductInventoryHandler(IConfiguration configuration, IProductInventoryRepository ProductInventoryRepository,
            IInboundBillRepository inboundBillRepository,
            IContractDiscountLevelRepository contractDiscountLevelRepository,
            IProductBillRepository producBillRepository,
            IBus bus, IBatchRepository batchRepository)
        {
            var connectionString = configuration.GetConnectionString("InventoryConnection");
            _dbConnection = new SqlConnection(connectionString);
            _ProductInventoryRepository = ProductInventoryRepository;
            _bus = bus;
            _batchRepository = batchRepository;
            _producBillRepository = producBillRepository;
            _contractDiscountLevelRepository = contractDiscountLevelRepository;
            _inboundBillRepository = inboundBillRepository;
        }

        public async Task<BaseResult> Handle(UpdateProductInventoryCommand request, CancellationToken cancellationToken)
        {
            var rs = new BaseResult();

            if (request.Ids.Count() > 200)
            {
                var length = request.Ids.Count();
                if (length > 0)
                {
                    for (int i = 0; i <= length / 200; i++)
                    {
                        if (length > i * 200)
                        {
                            var ids1 = request.Ids.Skip(i * 200).Take(200).ToList();
                            if (ids1.Count() > 0)
                            {
                                await _bus.SendAsync($"UpdateToElasticProductInventoryEx", ids1.ConvertListLongToString());
                            }
                        }
                    }
                }
                return new BaseResult { Result = Dto.Results.Result.Success, Message = $"sent {length} update ProductInventorys" };
            }

            var ids = request.Ids;

            var contractProductids = new List<long>();
            if (true)
            {
                var queryPI = new BoolQueryDescriptor<ContractDiscountLevelElastic>();
                var aggsPI = new AggregationContainerDescriptor<ContractDiscountLevelElastic>();
                var isDeletedPIQ = new QueryContainerDescriptor<ContractDiscountLevelElastic>();
                isDeletedPIQ.Terms(t => t.Field(f => f.IsDeleted).Terms(false));
                aggsPI.Terms("product", t => t.Field(f => f.ProductId).Size(10000));
                queryPI.Filter(f => isDeletedPIQ);
                var rsPI = await _contractDiscountLevelRepository.SearchAgg(queryPI, aggsPI);
                var aggPI = rsPI.Aggregations.Terms("product");
                if (aggPI != null)
                {
                    contractProductids = aggPI.Buckets.Select(x => long.Parse(x.Key)).ToList();
                }
            }
            var contractProductQ = new QueryContainerDescriptor<ProductInventoryElastic>();
            contractProductQ.Terms(e => e.Field(f => f.ProductId).Terms(contractProductids));
            var notInventoryCodeBillCreatedTimeQ = new QueryContainerDescriptor<ProductInventoryElastic>();
            notInventoryCodeBillCreatedTimeQ.Bool(b => b.MustNot(m => m.Exists(e => e.Field(f => f.InventoryCodeInboundCreatedTime))));
            if (!ids.Any())
            {
                //lấy danh sách k có ngày hết hạn
                var query = new BoolQueryDescriptor<ProductInventoryElastic>();
                var notProductBillPriceQ = new QueryContainerDescriptor<ProductInventoryElastic>();
                notProductBillPriceQ.Bool(b => b.MustNot(m => m.Exists(e => e.Field(f => f.ProductBillPrice))));
                var notProductBillVocherDateToLongQ = new QueryContainerDescriptor<ProductInventoryElastic>();
                notProductBillVocherDateToLongQ.Bool(b => b.MustNot(m => m.Exists(e => e.Field(f => f.ProductBillVocherDateToLong))));
                var quantityQ = new QueryContainerDescriptor<ProductInventoryElastic>();
                quantityQ.Range(r => r.Field(f => f.Quantity).GreaterThan(0));
                var notDelete1Q = new QueryContainerDescriptor<ProductInventoryElastic>();
                notDelete1Q.Terms(t => t.Field(f => f.IsDeleted).Terms(false));

                var idQ = new QueryContainerDescriptor<ProductInventoryElastic>();
                if (request.Ids.Any())
                    idQ.Terms(t => t.Field(f => f.Id).Terms(request.Ids));

                if (request.Ids.Any())
                    query.Filter(f => idQ);
                else if (contractProductids.Any())
                    query.Filter(f => notDelete1Q && (notProductBillPriceQ || notProductBillVocherDateToLongQ || ((contractProductQ && notInventoryCodeBillCreatedTimeQ))) && quantityQ);
                else query.Filter(f => notDelete1Q && (notProductBillPriceQ || notProductBillVocherDateToLongQ) && quantityQ);

                var ins = await _ProductInventoryRepository.Search(query, 2000);
                if (ins.Items.Any())
                    ids = ins.Items.Select(x => x.Id).ToList();
            }

            if (!ids.Any())
            {
                rs.Message = $"no data to update";
                rs.Result = Dto.Results.Result.Success;
                return rs;
            }

            try
            {
                var p = new Dto.DapperTypes.IdsParam(ids);
                var ProductInventorys = await _dbConnection.QueryAsync<ProductInventory>("Sync_ProductInventory_Get_By_List_Id", p, commandType: CommandType.StoredProcedure);
                if (ProductInventorys.Any())
                {
                    var items = ProductInventorys.ToProductInventorys();

                    //lấy thông tin ngày hết hạn
                    var queryBatch = new BoolQueryDescriptor<BatchElastic>();
                    var inventoryCodes = items.Where(x => !string.IsNullOrEmpty(x.InventoryCode) && !string.IsNullOrWhiteSpace(x.InventoryCode)).Select(x => x.InventoryCode).Distinct().ToList();
                    var inventoryCodeQ = new QueryContainerDescriptor<BatchElastic>();
                    if (inventoryCodes.Any())
                        inventoryCodeQ.Terms(e => e.Field(f => f.InventoryCode.Suffix("keyword")).Terms(inventoryCodes));

                    var productIds = items.Select(x => x.ProductId).Distinct().ToList();
                    var productInQ = new QueryContainerDescriptor<BatchElastic>();
                    productInQ.Terms(e => e.Field(f => f.ProductId).Terms(productIds));
                    var notDeleteQ = new QueryContainerDescriptor<BatchElastic>();
                    notDeleteQ.Terms(t => t.Field(f => f.IsDeleted).Terms(false));
                    queryBatch.Filter(f => inventoryCodeQ && productInQ && notDeleteQ);
                    var batches = await _batchRepository.Search(queryBatch, 10000);

                    //lay ra productbill
                    var queryProductBill = new BoolQueryDescriptor<ProductBillElastic>();
                    var notDeleteProductBillQ = new QueryContainerDescriptor<ProductBillElastic>();
                    notDeleteProductBillQ.Terms(t => t.Field(f => f.IsDeleted).Terms(false));
                    var productProductBillQ = new QueryContainerDescriptor<ProductBillElastic>();
                    productProductBillQ.Terms(e => e.Field(f => f.ProductId).Terms(productIds));
                    var booktypeQ = new QueryContainerDescriptor<ProductBillElastic>();
                    booktypeQ.Terms(e => e.Field(f => f.BookType).Terms(2));
                    var priceQ = new QueryContainerDescriptor<ProductBillElastic>();
                    priceQ.Range(e => e.Field(f => f.Price).GreaterThan(0));
                    queryProductBill.Filter(f => notDeleteProductBillQ && productProductBillQ && booktypeQ && priceQ);
                    var aggsProductBill = new AggregationContainerDescriptor<ProductBillElastic>();
                    aggsProductBill.Terms("product", s2 => s2.Field(f2 => f2.ProductId).Size(10000).Aggregations(a2 => a2.Max("id", s2 => s2.Field(f2 => f2.VocherDateToLong))));
                    var rsprBIlls = await _producBillRepository.SearchAgg(queryProductBill, aggsProductBill);

                    var listvocher = new List<long>();
                    if (rsprBIlls != null)
                    {
                        var r = rsprBIlls.Aggregations.Terms("product");
                        foreach (var item in r.Buckets)
                        {
                            var productInven = items.Where(x => x.ProductId == long.Parse(item.Key)).ToList();
                            var r2 = item.Max("id");
                            if (r2.Value.HasValue)
                            {
                                foreach (var item2 in productInven)
                                {
                                    item2.ProductBillVocherDateToLong = (long)r2.Value.Value;
                                }
                                listvocher.Add((long)r2.Value.Value);
                            }
                        }
                    }

                    var listproducBills = new List<ProductBillElastic>();
                    if (listvocher.Any())
                    {
                        var vocherQ = new QueryContainerDescriptor<ProductBillElastic>();
                        vocherQ.Terms(e => e.Field(f => f.VocherDateToLong).Terms(listvocher));
                        queryProductBill.Filter(f => notDeleteProductBillQ && vocherQ && productProductBillQ);

                        var r3 = await _producBillRepository.Search(queryProductBill, 10000);
                        if (r3.Items.Any()) listproducBills = r3.Items.ToList();
                    }

                    var inbounds = new List<InboundBillElastic>();
                    if (contractProductids.Any())
                    {
                        var queryPI = new BoolQueryDescriptor<InboundBillElastic>();
                        var isDeletedPIQ = new QueryContainerDescriptor<InboundBillElastic>();
                        isDeletedPIQ.Terms(t => t.Field(f => f.IsDeleted).Terms(false));
                        var productIQ = new QueryContainerDescriptor<InboundBillElastic>();
                        productIQ.Terms(t => t.Field(f => f.ProductId).Terms(contractProductids));
                        var billStatusIQ = new QueryContainerDescriptor<InboundBillElastic>();
                        billStatusIQ.Terms(t => t.Field(f => f.BillStatus).Terms(3));
                        var inboundStatusIQ = new QueryContainerDescriptor<InboundBillElastic>();
                        inboundStatusIQ.Terms(t => t.Field(f => f.InboundStatus).Terms(3));
                        var billTypeIQ = new QueryContainerDescriptor<InboundBillElastic>();
                        billTypeIQ.Terms(t => t.Field(f => f.BillType).Terms(1));

                        var codes = items.Where(x => contractProductids.Contains(x.ProductId)).Select(x => x.InventoryCode).Distinct().ToList();
                        var inventoryCodeIQ = new QueryContainerDescriptor<InboundBillElastic>();
                        if (codes.Any())
                            inventoryCodeIQ.Terms(e => e.Field(f => f.InventoryCode.Suffix("keyword")).Terms(codes));
                        if (codes.Any())
                        {
                            queryPI.Filter(f => isDeletedPIQ && productIQ && billStatusIQ && inboundStatusIQ && inventoryCodeIQ && billTypeIQ);
                            var rsPI = await _inboundBillRepository.Search(queryPI, 10000);
                            inbounds = rsPI.Items.ToList();
                        }
                    }

                    int updated = 0;
                    foreach (var item in items)
                    {
                        if (batches.Items.Any())
                        {
                            var br = batches.Items.FirstOrDefault(x => x.InventoryCode == item.InventoryCode && x.ProductId == item.ProductId);
                            if (br != null)
                                item.ExpiryDate = br.ExpiryDate;
                        }

                        if (listproducBills.Any())
                        {
                            var br = listproducBills.FirstOrDefault(x => x.ProductId == item.ProductId && x.VocherDateToLong == item.ProductBillVocherDateToLong);
                            if (br != null)
                            {
                                //item.ProductBillPrice = br.InventoryQuantity > 0 ? br.InventoryPrice / br.InventoryQuantity * item.Quantity : 0;
                                item.ProductBillPrice = /*br.InventoryQuantity > 0 ? br.InventoryPrice / br.InventoryQuantity*/ br.Price * item.Quantity/* : 0*/;
                            }
                        }

                        var inbound = inbounds.FirstOrDefault(x => x.InventoryCode == item.InventoryCode);
                        if (inbound != null)
                        {
                            item.InventoryCodeInboundCreatedTime = inbound.CreatedTime;
                            item.InventoryCodeBillId = inbound.BillId;
                        }

                        updated++;
                    }

                    var rs2 = await _ProductInventoryRepository.AddManyAsync(items);
                    if (rs2.Result == Dto.Results.Result.Success)
                    {
                        rs.Message = $"{updated} update";
                        rs.Result = Dto.Results.Result.Success;
                    }
                    else
                    {
                        rs.Message = $"can not insert db";
                        rs.Result = Dto.Results.Result.Success;
                    }
                }
                else
                {
                    rs.Message = $"no data to update";
                    rs.Result = Dto.Results.Result.Success;
                }
            }
            catch (Exception ex)
            {
                rs.Message = ex.Message.ToString();
                rs.Result = Dto.Results.Result.SystemError;
            }
            return rs;
        }
    }
}