using Bp.Sync.BackEnd.Infrastructure.Repositories;
using Bp.Sync.BackEnd.Infrastructure.Repositories.FcBusinesses;
using Bp.Sync.BackEnd.Infrastructure.Repositories.FcCustomers;
using Bp.Sync.Domain.Elastics.FcBusinesses;
using Bp.Sync.Domain.Elastics.FcBusinesses.Products;
using Bp.Sync.Domain.Entities.FcBusinesses;
using Bp.Sync.Dto.Results;
using CommonLibrary;
using Dapper;
using EasyNetQ;
using Microsoft.Extensions.Configuration;
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.RetailStatistics
{
    public class SyncRetailStatisticHandler : MediatR.IRequestHandler<SyncRetailStatisticCommand, BaseResult>
    {
        private readonly IDbConnection _dbConnection;
        private readonly IRetailStatisticRepository _RetailStatisticRepository;
        private readonly IBus _bus;
        private readonly IProductRepository _productRepository;
        private readonly IFcReceiptBillRepository _fcReceiptBillRepository;
        private readonly IUnitRepository _unitRepository;
        private readonly IFcCustomerRepository _fcCustomerRepository;
        private readonly IFcProductStorageRepository _fcProductStorageRepository;

        public SyncRetailStatisticHandler(IConfiguration configuration, IRetailStatisticRepository RetailStatisticRepository, IBus bus,
            IProductRepository productRepository,
            IFcReceiptBillRepository fcReceiptBillRepository,
            IUnitRepository unitRepository,
            IFcProductStorageRepository fcProductStorageRepository,
            IFcCustomerRepository fcCustomerRepository)
        {
            var connectionString = configuration.GetConnectionString("FcBusinessConnection");
            _dbConnection = new SqlConnection(connectionString);
            _RetailStatisticRepository = RetailStatisticRepository;
            _bus = bus;
            _productRepository = productRepository;
            _fcReceiptBillRepository = fcReceiptBillRepository;
            _unitRepository = unitRepository;
            _fcCustomerRepository = fcCustomerRepository;
            _fcProductStorageRepository = fcProductStorageRepository;
        }

        public async Task<BaseResult> Handle(SyncRetailStatisticCommand 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 ids = request.Ids.Skip(i * 1000).Take(1000).ToList();
                            if (ids.Count() > 0)
                            {
                                await _bus.SendAsync($"SyncToElasticRetailStatistic", ids.ConvertListLongToString());
                            }
                        }
                    }
                }
                return new BaseResult { Result = Result.Success, Message = $"sent {length} RetailStatistics" };
            }
            if (!request.Ids.Any())
            {
                rs.Message = $"no data to update";
                rs.Result = Dto.Results.Result.Success;
                return rs;
            }

            try
            {
                var p = new Dto.DapperTypes.IdsParam(request.Ids);
                var RetailStatistics = await _dbConnection.QueryAsync<RetailStatistic>("Sync_RetailStatistic_Get_By_List_Id", p, commandType: CommandType.StoredProcedure);
                if (RetailStatistics.Any())
                {
                    var listUpdated = new List<RetailStatisticElastic>();

                    var items = RetailStatistics.ToRetailStatistics();

                    var productIds = items.Select(x => x.ProductId).Distinct().ToList();
                    var billIds = items.Select(x => x.BillId).Distinct().ToList();
                    var products = await _productRepository.GetByIds(productIds);
                    var bills = await _fcReceiptBillRepository.GetByIds(billIds);

                    var units = await _unitRepository.GetAll();

                    var customerids = items.Where(x => x.CustomerId.HasValue).Select(x => x.CustomerId.Value).Distinct().ToList();
                    var customers = await _fcCustomerRepository.GetByIds(customerids.Select(x => (long)x).ToList());

                    foreach (var item in items)
                    {
                        if (item.RetailBaseUnitCostPrice == 0) item.RetailBaseUnitCostPrice = item.RetailBaseUnitCost;

                        var bill = bills.Items.FirstOrDefault(x => x.Id == item.BillId);
                        if (bill != null)
                        {
                            if (!item.StatisticStatus.HasValue)
                            {
                                if (bill.CompletedDate.HasValue)
                                    item.StatisticStatus = StatisticStatus.Finished;
                                else
                                    item.StatisticStatus = StatisticStatus.Processing;
                            }

                            item.BillPaymentMode = bill.PaymentMode;
                            item.BillCode = bill.Code;
                            item.BillType = bill.BillType;
                            if (item.BillType.HasValue && item.BillType == Domain.Entities.FcBusinesses.Bills.BillType.CustomerReturnBill) item.RetailTotalPrice = item.RetailTotalPrice * (-1);
                        }

                        var unit = units.Items.FirstOrDefault(x => x.Id == item.RetailUnitId);
                        item.RetailUnitName = unit?.Name;

                        var unitOriginal = units.Items.FirstOrDefault(x => x.Id == item.BaseUnitId);
                        item.BaseUnitName = unitOriginal?.Name;

                        var prd = products.Items.FirstOrDefault(x => x.Id == item.ProductId);
                        item.ProductName = prd?.Name;
                        item.ProductCode = prd?.Code;
                        if (item.CustomerId.HasValue && item.CustomerId.Value > 0)
                        {
                            var cus = customers.Items.FirstOrDefault(x => x.Id == item.CustomerId.Value);
                            item.CustomerName = cus?.FullName;
                            item.CustomerUserName = cus?.UserName;
                            item.CustomerPhoneNumber = cus?.PhoneNumber;
                        }
                        else
                        {
                            item.CustomerName = "Khách lẻ";
                            item.CustomerUserName = "Khách lẻ";
                            item.CustomerUserName = "";
                        }

                        #region tính giá vốn

                        decimal basecost = 0;
                        decimal quantity = 0;

                        var query = new Nest.BoolQueryDescriptor<FcProductStorageElastic>();
                        var aggs = new Nest.AggregationContainerDescriptor<FcProductStorageElastic>();

                        var notDelete1Q = new Nest.QueryContainerDescriptor<FcProductStorageElastic>();
                        notDelete1Q.Terms(t => t.Field(f => f.IsDeleted).Terms(false));
                        var productQ = new Nest.QueryContainerDescriptor<FcProductStorageElastic>();
                        productQ.Terms(t => t.Field(f => f.ProductId).Terms(item.ProductId));
                        var branchQ = new Nest.QueryContainerDescriptor<FcProductStorageElastic>();
                        branchQ.Terms(t => t.Field(f => f.BranchId).Terms(item.BranchId));
                        var quantityQ = new Nest.QueryContainerDescriptor<FcProductStorageElastic>();
                        quantityQ.Range(t => t.Field(f => f.CurrentQuantity).GreaterThan(0));

                        query.Filter(fl => productQ && notDelete1Q && quantityQ && branchQ);
                        aggs.Sum("cost", s2 => s2.Field(f2 => f2.TotalCost));
                        var rs2 = await _fcProductStorageRepository.SearchAgg(query, aggs);
                        var aggx = rs2.Aggregations.Sum("cost");
                        if (aggx != null && aggx.Value.HasValue)
                        {
                            basecost += (decimal)aggx.Value.Value;
                        }
                        aggs.Sum("quantity", s2 => s2.Field(f2 => f2.CurrentQuantity));
                        rs2 = await _fcProductStorageRepository.SearchAgg(query, aggs);
                        aggx = rs2.Aggregations.Sum("quantity");
                        if (aggx != null && aggx.Value.HasValue)
                        {
                            quantity += (decimal)aggx.Value.Value;
                        }

                        if (basecost > 0 && quantity > 0) item.RetailBaseUnitCostPrice = basecost / quantity;
                        else item.RetailBaseUnitCostPrice = item.RetailBaseUnitCost;

                        #endregion tính giá vốn

                        listUpdated.Add(item);
                    }
                    if (listUpdated.Any())
                    {
                        var rs2 = await _RetailStatisticRepository.AddManyAsync(listUpdated);
                        if (rs2.Result == Result.Success)
                        {
                            await _dbConnection.ExecuteAsync("Synced_RetailStatistic_By_List_Id", p, commandType: CommandType.StoredProcedure);
                            rs.Message = $"{items.Count()} synced";
                            rs.Result = Result.Success;
                            return rs;
                        }
                        else
                        {
                            rs.Message = $"can not insert db";
                            rs.Result = Result.Success;
                            return rs;
                        }
                    }
                    else
                    {
                        rs.Message = $"no data to update";
                        rs.Result = Result.Success;
                        return rs;
                    }
                }
                else
                {
                    rs.Message = $"no data to update";
                    rs.Result = Result.Success;
                    return rs;
                }
            }
            catch (Exception ex)
            {
                rs.Message = ex.Message.ToString();
                rs.Result = Result.SystemError;
            }
            return rs;
        }
    }
}