using Bp.Sync.BackEnd.Infrastructure.Repositories;
using Bp.Sync.Domain.Elastics.Inventories.Orders;
using Bp.Sync.Domain.Entities.Inventories;
using Bp.Sync.Domain.Entities.Inventories.Branches;
using Bp.Sync.Domain.Entities.Inventories.Customers;
using Bp.Sync.Domain.Entities.Inventories.Suppliers;
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;
using Result = Bp.Sync.Dto.Results.Result;

namespace Bp.Sync.BackEnd.Application.Commands.Bills.Bills
{
    public class SyncBillHandler : IRequestHandler<SyncBillCommand, BaseResult>
    {
        private readonly IDbConnection _dbConnection;
        private readonly IBillRepository _billRepository;
        private readonly IOrderRepository _orderRepository;
        private readonly IBus _bus;

        public SyncBillHandler(IConfiguration configuration, IBillRepository billRepository, IOrderRepository orderRepository, IBus bus)
        {
            var connectionString = configuration.GetConnectionString("InventoryConnection");
            _dbConnection = new SqlConnection(connectionString);
            _billRepository = billRepository;
            _orderRepository = orderRepository;
            _bus = bus;
        }

        public async Task<BaseResult> Handle(SyncBillCommand 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($"SyncToElasticBill0", ids.ConvertListLongToString());
                            }
                        }
                    }
                }
                return new BaseResult { Result = Result.Success, Message = $"sent {length} orders" };
            }
            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 bills = await _dbConnection.QueryAsync<Bill>("Sync_Bill_Get_By_List_Id", p, commandType: CommandType.StoredProcedure);
                if (bills.Any())
                {
                    var billRs = bills.ToBills();

                    try
                    {
                        var customerids = billRs.Where(x => x.CustomerId > 0).Select(x => (long)x.CustomerId).Distinct().ToList();
                        if (customerids.Any())
                        {
                            var p2 = new Dto.DapperTypes.IdsParam(customerids);
                            var customers = await _dbConnection.QueryAsync<Customer>("Sync_Customer_Get_By_List_Id", p2, commandType: CommandType.StoredProcedure);
                            if (customers.Any())
                            {
                                foreach (var item in billRs)
                                {
                                    if (item.CustomerId > 0)
                                        item.CustomerName = customers.FirstOrDefault(x => x.Id == item.CustomerId)?.Name;
                                }
                            }
                        }

                        var branchids = billRs.Where(x => x.DestBranchId > 0).Select(x => (long)x.DestBranchId).Distinct().ToList();
                        if (branchids.Any())
                        {
                            var p2 = new Dto.DapperTypes.IdsParam(branchids);
                            var branchs = await _dbConnection.QueryAsync<Branch>("Sync_Branch_Get_By_List_Id", p2, commandType: CommandType.StoredProcedure);
                            if (branchs.Any())
                            {
                                foreach (var item in billRs)
                                {
                                    if (item.DestBranchId > 0)
                                        item.DestBranchName = branchs.FirstOrDefault(x => x.Id == item.DestBranchId)?.Name;
                                }
                            }
                        }
                        var supplierids = billRs.Where(x => x.SupplierId > 0).Select(x => (long)x.SupplierId).Distinct().ToList();
                        if (supplierids.Any())
                        {
                            var p2 = new Dto.DapperTypes.IdsParam(supplierids);
                            var supplierss = await _dbConnection.QueryAsync<Supplier>("Sync_Supplier_Get_By_List_Id", p2, commandType: CommandType.StoredProcedure);
                            if (supplierss.Any())
                            {
                                foreach (var item in billRs)
                                {
                                    if (item.SupplierId > 0)
                                        item.SupplierName = supplierss.FirstOrDefault(x => x.Id == item.SupplierId)?.Name;
                                }
                            }
                        }
                    }
                    catch { }

                    var billRelateIds = bills.Where(x => x.RelateId > 0).Select(x => x.RelateId).Distinct().ToList();

                    var billRelates = new List<Bill>();
                    if (billRelateIds.Any())
                    {
                        var p3 = new Dto.DapperTypes.IdsParam(billRelateIds);
                        var billRelate2s = await _dbConnection.QueryAsync<Bill>("Sync_Bill_Get_By_List_Id", p3, commandType: CommandType.StoredProcedure);
                        if (billRelate2s.Any()) billRelates = billRelate2s.ToList();
                    }

                    foreach (var item in billRs)
                    {
                        if (item.RelateId > 0)
                        {
                            var billrelate = billRelates.FirstOrDefault(x => x.Id == item.RelateId);
                            if (billrelate != null && billrelate.MedrepId > 0)
                            {
                                item.IsMedrepReturn = true;
                                item.IdMedrepReturn = billrelate.MedrepId;
                            }
                        }
                    }

                    var rs2 = await _billRepository.AddManyAsync(billRs);
                    if (rs2.Result == Result.Success)
                    {
                        await _dbConnection.ExecuteAsync("Synced_Bill_By_List_Id", p, commandType: CommandType.StoredProcedure);
                        rs.Message = $"{billRs.Count()} synced";
                        rs.Result = Result.Success;

                        var bids = billRs.Select(x => x.Id).ToList();
                        await _bus.SendAsync($"UpdateChildBillByBill", bids.ConvertListLongToString());

                        //đồng bộ lại đơn bán buôn để lấy trạng thái thu tiền cho báo cáo
                        var wscodes = billRs.Where(x => !string.IsNullOrEmpty(x.OrderCode) && !string.IsNullOrWhiteSpace(x.OrderCode)).Select(x => x.OrderCode).Distinct().ToList();
                        if (wscodes.Any())
                        {
                            var query = new Nest.BoolQueryDescriptor<OrderElastic>();
                            var codeQ = new Nest.QueryContainerDescriptor<OrderElastic>();
                            codeQ.Terms(t => t.Field(f => f.Code.Suffix("keyword")).Terms(wscodes));
                            var isDeletedQ = new Nest.QueryContainerDescriptor<OrderElastic>();
                            isDeletedQ.Terms(e => e.Field(f => f.IsDeleted).Terms(false));
                            query.Filter(f => codeQ && isDeletedQ);
                            var orders = await _orderRepository.Search(query, 10000);
                            if (orders.Items.Any())
                            {
                                var orderids = orders.Items.Select(x => x.Id).ToList();
                                await _bus.SendAsync($"UpdateOrderById", orderids.ConvertListLongToString());
                            }
                        }
                    }
                    else
                    {
                        rs.Message = $"can not insert db";
                        rs.Result = Result.Success;
                    }
                }
                else
                {
                    rs.Message = $"no data to update";
                    rs.Result = Result.Success;
                }
            }
            catch (Exception ex)
            {
                rs.Message = ex.Message.ToString();
                rs.Result = Result.SystemError;
            }
            return rs;
        }
    }
}