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 System.Collections.Generic;
using Nest;
using Bp.Sync.Domain.Elastics.Inventories.Bills;
using Microsoft.Extensions.Configuration;
using Dapper;
using System.Data.SqlClient;
using Vina.Search.Infrastructure.Mappings;
using Bp.Sync.Domain.Entities.Inventories;

namespace Bp.Sync.BackEnd.Application.Commands.Inventories.Bills.InventoryBills
{
    public class UpdateInventoryBillHandler : IRequestHandler<UpdateInventoryBillCommand, BaseResult>
    {
        private readonly IInventoryBillRepository _InventoryBillRepository;
        private readonly IBillRepository _billRepository;
        private readonly IProductRepository _productRepository;
        private readonly IUnitRepository _unitRepository;
        private readonly IDbConnection _dbConnection;

        public UpdateInventoryBillHandler(IInventoryBillRepository InventoryBillRepository, IBillRepository billRepository,
            IConfiguration configuration,
            IProductRepository productRepository, IUnitRepository unitRepository)
        {
            var connectionString = configuration.GetConnectionString("InventoryConnection");
            _dbConnection = new SqlConnection(connectionString);
            _InventoryBillRepository = InventoryBillRepository;
            _billRepository = billRepository;
            _productRepository = productRepository;
            _unitRepository = unitRepository;
        }
        public async Task<BaseResult> Handle(UpdateInventoryBillCommand request, CancellationToken cancellationToken)
        {
            var rs = new BaseResult();

            var listUpdated = new List<InventoryBillElastic>();

            //lay ra InventoryBill k co invoice
            var query = new BoolQueryDescriptor<InventoryBillElastic>();
            var notBranchQ = new QueryContainerDescriptor<InventoryBillElastic>();
            var isDeletedQ = new QueryContainerDescriptor<InventoryBillElastic>();
            isDeletedQ.Terms(t => t.Field(f => f.IsDeleted).Terms(false));

            notBranchQ.Bool(b => b.MustNot(m => m.Exists(e => e.Field(f => f.BranchId))));
            var notBillCOdeQ = new QueryContainerDescriptor<InventoryBillElastic>();
            notBillCOdeQ.Bool(b => b.MustNot(m => m.Exists(e => e.Field(f => f.BillCode))));
            var notProductCOdeQ = new QueryContainerDescriptor<InventoryBillElastic>();
            notProductCOdeQ.Bool(b => b.MustNot(m => m.Exists(e => e.Field(f => f.ProductCode))));
            var notUnitNameQ = new QueryContainerDescriptor<InventoryBillElastic>();
            notUnitNameQ.Bool(b => b.MustNot(m => m.Exists(e => e.Field(f => f.UnitName))));
            var notDifferenceQuantityQ = new QueryContainerDescriptor<InventoryBillElastic>();
            notDifferenceQuantityQ.Bool(b => b.MustNot(m => m.Exists(e => e.Field(f => f.DifferenceQuantity))));
            var notBillIsDeletedQ = new QueryContainerDescriptor<InventoryBillElastic>();
            notBillIsDeletedQ.Bool(b => b.MustNot(m => m.Exists(e => e.Field(f => f.BillIsDeleted))));

            var idQ = new QueryContainerDescriptor<InventoryBillElastic>();
            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 => isDeletedQ && (notBranchQ || notBillCOdeQ || notProductCOdeQ || notUnitNameQ || notDifferenceQuantityQ || notBillIsDeletedQ));
            var items = await _InventoryBillRepository.Search(query, 5000);

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

            var p = new Dto.DapperTypes.IdsParam(items.Items.Select(x => x.Id).ToList());
            var InventoryBills = await _dbConnection.QueryAsync<Domain.Entities.Inventories.Bills.InventoryBill>("Sync_InventoryBill_Get_By_List_Id", p, commandType: CommandType.StoredProcedure);
            if (!InventoryBills.Any()) return new BaseResult { Result = Dto.Results.Result.Failed, Message = "no data" };

            var InventoryBillRs = InventoryBills.ToInventoryBills();

            var billIds = InventoryBillRs.Select(x => x.BillId).Distinct().ToList();
            var p2 = new Dto.DapperTypes.IdsParam(billIds);
            var bills = await _dbConnection.QueryAsync<Bill>("Sync_Bill_Get_By_List_Id", p2, commandType: CommandType.StoredProcedure);

            var productIds = InventoryBillRs.Select(x => x.ProductId).Distinct().ToList();
            var products = await _productRepository.GetByIds(productIds);
            var unitIds = InventoryBillRs.Select(x => (long)x.UnitId).Distinct().ToList();
            var units = await _unitRepository.GetAll();

            if (bills != null && bills.Any())
            {
                foreach (var item in InventoryBillRs)
                {
                    var bill = bills.FirstOrDefault(x => x.Id == item.BillId);
                    if (bill != null)
                    {
                        item.BillIsDeleted = bill.IsDeleted;
                        item.BillStatus = bill.BillStatus;
                        item.BillCreatedTime = bill.CreatedTime;
                        item.BillType = bill.BillType;
                        item.BillCode = bill.Code;
                        item.BranchId = bill.BranchId;
                    }
                    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;
                    }

                    listUpdated.Add(item);
                }
            }

            if (listUpdated.Any())
            {
                var rs2 = await _InventoryBillRepository.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;
        }
    }
}
