using Bp.Sync.BackEnd.Infrastructure.Repositories;
using Bp.Sync.BackEnd.Infrastructure.Repositories.FcBusinesses;
using Bp.Sync.Domain.Elastics.FcBusinesses.Products;
using Bp.Sync.Domain.Elastics.Inventories;
using Bp.Sync.Domain.Elastics.Inventories.Products;
using Bp.Sync.Domain.Entities.Inventories.Products;
using Bp.Sync.Dto.Results;
using Bp.Sync.Dto.SmartTable;
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.FcBusinesses.Products
{
    public class UpdateExpiryDateFcProductStorageHandler : IRequestHandler<UpdateExpiryDateFcProductStorageCommand, BaseResult>
    {
        private readonly IDbConnection _dbConnection;
        private readonly IFcProductStorageRepository _FcProductStorageRepository;
        private readonly IBatchRepository _batchRepository;
        private readonly IBus _bus;
        private readonly IMediator _mediator;

        public UpdateExpiryDateFcProductStorageHandler(IConfiguration configuration, IFcProductStorageRepository FcProductStorageRepository,
            IMediator mediator, IBus bus, IBatchRepository batchRepository)
        {
            var connectionString = configuration.GetConnectionString("InventoryConnection");
            _dbConnection = new SqlConnection(connectionString);
            _FcProductStorageRepository = FcProductStorageRepository;
            _bus = bus;
            _batchRepository = batchRepository;
            _mediator = mediator;
        }
        public async Task<BaseResult> Handle(UpdateExpiryDateFcProductStorageCommand request, CancellationToken cancellationToken)
        {
            var rs = new BaseResult();

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

            var ids = request.Ids;
            var listUpdates = new List<long>();

            var batches = new SmartTableResult<BatchElastic> { Items = new List<BatchElastic>() };
            if (!ids.Any())
            {
                //lay ra danh sach don k co expiry date
                var query = new BoolQueryDescriptor<FcProductStorageElastic>();
                var notExpiryQ = new QueryContainerDescriptor<FcProductStorageElastic>();
                notExpiryQ.Bool(b => b.MustNot(m => m.Exists(e => e.Field(f => f.ExpiryDate))));
                var quantity0Q = new QueryContainerDescriptor<FcProductStorageElastic>();
                quantity0Q.Range(e => e.Field(f => f.CurrentQuantity).GreaterThan(0));
                var notDelete1Q = new QueryContainerDescriptor<FcProductStorageElastic>();
                notDelete1Q.Terms(t => t.Field(f => f.IsDeleted).Terms(false));

                var idQ = new QueryContainerDescriptor<ProductInvoiceElastic>();
                if (ids.Any()) idQ.Terms(t => t.Field(f => f.Id).Terms(request.Ids));
                if (ids.Any())
                    query.Filter(fl => idQ);
                else
                    query.Filter(f => notDelete1Q && notExpiryQ);
                var ins = await _FcProductStorageRepository.Search(query, 10000);

                var needs = ins.Items.OrderBy(x => x.Id).ToList();

                var step = 1000;
                var lenth = needs.Count / step;
                for (int i = 0; i <= lenth; i++)
                {
                    var productIds = new List<long>();
                    var codes = new List<string>();

                    var steps = needs.Skip(i * step).Take(step).ToList();
                    if (steps.Any())
                        productIds = steps.Select(x => x.ProductId).Distinct().ToList();
                    if (steps.Any())
                        codes = steps.Select(x => x.InventoryCode).Distinct().ToList();
                    //lấy thông tin ngày hết hạn
                    var queryBatch = new BoolQueryDescriptor<BatchElastic>();
                    var inventoryCodeQ = new QueryContainerDescriptor<BatchElastic>();
                    if (codes.Any())
                        inventoryCodeQ.Terms(e => e.Field(f => f.InventoryCode.Suffix("keyword")).Terms(codes));

                    var productInQ = new QueryContainerDescriptor<BatchElastic>();
                    productInQ.Terms(e => e.Field(f => f.ProductId).Terms(ids));
                    var notDeleteQ = new QueryContainerDescriptor<BatchElastic>();
                    notDeleteQ.Terms(t => t.Field(f => f.IsDeleted).Terms(false));

                    queryBatch.Filter(f => inventoryCodeQ && productInQ && notDeleteQ);
                    var batches2 = await _batchRepository.Search(queryBatch, 10000);
                    if (batches2.Items.Any()) batches.Items = batches.Items.Concat(batches2.Items);
                }

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




                var prIds = batches.Items.Select(x => x.ProductId).Distinct().ToList();

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

                ins.Items = ins.Items.Where(x => prIds.Contains(x.ProductId)).ToList();

                foreach (var item in ins.Items)
                {
                    var br = batches.Items.FirstOrDefault(x => x.InventoryCode == item.InventoryCode && x.ProductId == item.ProductId);
                    if (br != null) listUpdates.Add(item.Id);
                }
            }

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

            var r = await _mediator.Send(new UpdateFcProductStorageCommand(listUpdates));
            return r;
        }
    }
}
