using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using XYY.Core.Standard.Data.Infrastructure;
using XYY.Service.Standard.Finance.Charging.Base;
using XYY.Service.Standard.Finance.Charging.EUExchange;
using XYY.Service.Standard.Finance.Charging.Express;
using XYY.Service.Standard.Finance.Charging.FeeFormula;
using XYY.Service.Standard.Finance.Charging.UserRate;
using Dapper;
using XYY.Service.Standard.Finance.Charging.CustomerAccount;
using XYY.Core.Standard.Mvc;
using XYY.TaskTrack.Standard;
using XYY.Model.Standard.Enums;
using StackExchange.Redis;
using XYY.Core.Standard.Data.Redis;
using XYY.Service.Standard.Finance.Charging.Dto;
using XYY.Model.Standard.Charging;
using XYY.Data.Standard.Charging;
using System.Threading;

namespace XYY.Service.Standard.Finance.Charging.CustomerOrderFee
{
    public interface ICustomerOrderFeeService
    {
        Task<ApiJsonModel> Charges(ExpressFeechargesModel t);
        /// <summary>
        /// 对客户和客户所属的分公司进行计费扣费
        /// </summary>
        /// <param name="t"></param>
        /// <returns></returns>
        Task<ApiJsonModel> ChargeCustomerAndBranchCompany(ExpressFeechargesModel t);

        Task FixBranchCompanyCharge(List<string> trackingNumbers);

        Task AddNotSend(string periceVersion, string channelId, string customerId);
        Task ReChargeByTime(ReChargeByTimeParam param);
    }

    public class CustomerOrderFeeService : ICustomerOrderFeeService
    {
        private IFinance_Charge_LogRepository _finance_Charge_LogRepository;
        private IExpressFeeChargesService _chargesService;
        private IEUExchangeService _exchangeService;
        private IAttachedService _attachedService;
        private IFormulaService _formulaService;
        private IUserRateService _rateService;
        private IUnitOfWork _unitOfWork;
        private ICustomerAccountService _customerAccountService;
        private IMQManager _MQManager;
        IDatabase db;
        IServer server;
        public CustomerOrderFeeService(IExpressFeeChargesService chargesService,
            IEUExchangeService exchangeService, IAttachedService attachedService,
            IFormulaService formulaService, IUserRateService rateService, IUnitOfWork unitOfWork, ICustomerAccountService customerAccountService,
            IMQManager MQManager, RedisHelper redis, IFinance_Charge_LogRepository finance_Charge_LogRepository)
        {
            _chargesService = chargesService;
            _exchangeService = exchangeService;
            _attachedService = attachedService;
            _formulaService = formulaService;
            _rateService = rateService;
            _unitOfWork = unitOfWork;
            _customerAccountService = customerAccountService;
            _MQManager = MQManager;
            this.db = redis.GetDatabase();
            _finance_Charge_LogRepository = finance_Charge_LogRepository;
        }

        /// <summary>
        /// 对客户和客户所属的分公司进行计费扣费
        /// </summary>
        /// <param name="t"></param>
        /// <returns></returns>
        public async Task<ApiJsonModel> ChargeCustomerAndBranchCompany(ExpressFeechargesModel t)
        {
            //30秒内不重取
            string key = t.BillNo;
            bool flag = db.StringSet(t.BillNo
                , "1", TimeSpan.FromSeconds(30), When.NotExists, StackExchange.Redis.CommandFlags.None);
            if (flag)
            {
                string accountingNo = $"ANO{DateTime.Now.ToString("yyyyMMddHHmmss")}{t.OrderId}";
                t.AccountingNo = accountingNo;
                //先克隆一个，以免计费过程中t的值已被修改
                var tForBranchCompany = t.Copy<ExpressFeechargesModel>();

                var customerResult = await Charges(t);
                if (customerResult.success && t.BranchCompanyCustomerId.HasValue)
                {
                    t.CustomerId = t.BranchCompanyCustomerId.Value;
                    t.IsChargeBranchCompany = true;

                    await Charges(t);
                }
                return customerResult;
            }
            else
            {
                return ApiJsonModel.OK("成功");
            }
        }

        private async Task SendBranchCompanyChargeMessage(ExpressFeechargesModel feeFhargesModel, int branchCompanyCustomerId)
        {
            feeFhargesModel.CustomerId = branchCompanyCustomerId;
            feeFhargesModel.IsChargeBranchCompany = true;
            await _MQManager.Publish(feeFhargesModel);
        }

        /// <summary>
        /// 计费并扣费
        /// 为保证正确性，业务优先扣费，如业务中出现错误，再进行冲抵操作即可
        /// </summary>
        /// <param name="t">返回成功或欠费</param>
        /// <returns></returns>
        public async Task<ApiJsonModel> Charges(ExpressFeechargesModel t)
        {
            ThreadPool.SetMinThreads(200, 200);
            System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch();
            stopwatch.Start();
            t.timeList = new Dictionary<string, long>();
            if (t.AccountingDate.HasValue && t.AccountingDate.Value.Kind == DateTimeKind.Utc)
                t.AccountingDate = t.AccountingDate.Value.ToLocalTime();
            if (t.TransactionTime.Kind == DateTimeKind.Utc)
                t.TransactionTime = t.TransactionTime.ToLocalTime();

            if (string.IsNullOrWhiteSpace(t.AccountingNo))
            {
                string accountingNo = $"ANO{DateTime.Now.ToString("yyyyMMddHHmmss")}{t.OrderId}";
            }
            //t.AccountingNo = "ANO" + DateTime.Now.ToString("yyyyMMddHHmmssfff");

            if (!t.IsChargeBranchCompany && await _customerAccountService.IsArrears(t.CustomerId))
            {
                return ApiJsonModel.Error("欠费");
            }
            stopwatch.Stop();
            t.timeList.Add("欠费判断", stopwatch.ElapsedMilliseconds);
            stopwatch.Restart();

            t.CreateTime = DateTime.Now;
            t.TransactionType = TransactionType.Consume;
            t.IncomeOrESxpense = "支出";

            ChargeTarget chargeTarget = t.IsChargeBranchCompany ? ChargeTarget.分公司 : ChargeTarget.客户;
            /*订单运费明细*/
            Finance_Order_Charges finance_Order_Charges = new Finance_Order_Charges
            {
                ChannelId = t.ExpressId,
                OrderCreateChannelId = t.PublicExpressId,
                TransactionTime = t.TransactionTime,
                CountryCode = t.CountryCode,
                AccountingNo = t.AccountingNo,
                CustomerId = t.CustomerId,
                GoodsDeclaredVal = t.GoodsDeclared,
                SystemNo = t.BillNo,
                TrackingNumber = t.TrackingNumber,
                Weight = t.BillingWeight,
                Offset = false,
                FeeDescription = t.FeeDescription,
                IncomeOrESxpense = t.IncomeOrESxpense,
                HasIOSS = t.HasIOSS,
                ZipCode = t.ZipCode,
                ChargeTarget = chargeTarget,
                Length = t.Length,
                Height = t.Height,
                Width = t.Width,
            };
            string remark = string.Empty;
            //按当前时间生成交易号
            ///运费
            decimal shopFee = 0;
            //取运费
            var shopFeechargesModel = t.Copy<ExpressFeechargesModel>();

            shopFeechargesModel.TransactionNo = "OF" + DateTime.Now.ToString("yyyyMMddHHmmssfff");
            var fc = await _chargesService.Charges(shopFeechargesModel);

            stopwatch.Stop();
            t.timeList.Add("运费计算（无公式）", stopwatch.ElapsedMilliseconds);
            stopwatch.Restart();
            if (!fc.success)
                return fc;
            //按公式计算完整运费
            await _formulaService.Charges(shopFeechargesModel);
            shopFee = Math.Round(shopFeechargesModel.SettlementAmount, 2, MidpointRounding.AwayFromZero);
            shopFeechargesModel.SettlementAmount = shopFee;
            shopFeechargesModel.ChargeName = "运费(含燃油)";

            stopwatch.Stop();
            t.timeList.Add("公式计算", stopwatch.ElapsedMilliseconds);
            stopwatch.Restart();

            decimal tax = 0;
            //计算税费
            var eu = t.Copy<ExpressFeechargesModel>();
            eu.TransactionNo = "OF" + DateTime.Now.ToString("yyyyMMddHHmmssfff");
            await _exchangeService.Charges(eu);

            stopwatch.Stop();
            t.timeList.Add("税费计算(无汇率)", stopwatch.ElapsedMilliseconds);
            stopwatch.Restart();

            //换算汇率
            if (eu.AmountinOriginalCurrency > 0)
            {
                var rc = await _rateService.Charges(eu);
                stopwatch.Stop();
                t.timeList.Add("汇率转换", stopwatch.ElapsedMilliseconds);
                stopwatch.Restart();
                if (!rc.success)
                    return rc;
                tax = eu.SettlementAmount;
                eu.ChargeName = "税费";
            }

            //附加费
            decimal attache = 0;

            var at = t.Copy<ExpressFeechargesModel>();
            List<ExpressFeechargesModel> feeChargesList = new List<ExpressFeechargesModel>();


            //附加费明细,拆分备注
            if (t.PublicExpressId == 34 || t.PublicExpressId == 433 || t.PublicExpressId == 65 || t.PublicExpressId == 199)
            {
                remark = shopFeechargesModel.FeeDescription;
                if (string.IsNullOrEmpty(remark))
                {
                    remark = " ";
                }
                if (!string.IsNullOrEmpty(shopFeechargesModel.FeeDescription)
                    && shopFeechargesModel.FeeDescription.Contains('#')
                    )
                {

                    foreach (var item in shopFeechargesModel.FeeDescription.Split(','))
                    {
                        decimal.TryParse(item.Split("#")[2], out var fi);
                        //f += fi;
                        attache += fi;
                        var atitem = shopFeechargesModel.Copy<ExpressFeechargesModel>();
                        //t.SettlementAmount = f;
                        atitem.ChargeName = item.Split("#")[0];
                        atitem.Currency = item.Split("#")[1];
                        atitem.SettlementCurrency = at.Currency;
                        atitem.AmountinOriginalCurrency = fi;
                        atitem.SettlementAmount = fi;
                        feeChargesList.Add(atitem);
                    }
                }
            }
            else
            {
                at.TransactionNo = "OF" + DateTime.Now.ToString("yyyyMMddHHmmssfff");
                var ac = await _attachedService.Charges(at);
                stopwatch.Stop();
                t.timeList.Add("附加费计算", stopwatch.ElapsedMilliseconds);
                stopwatch.Restart();
                if (!ac.success)
                    return ac;
                attache = at.SettlementAmount;
                at.ChargeName = "附加费";
            }

            if (attache > 0)
            {
                attache = Math.Round(attache, 2, MidpointRounding.AwayFromZero);
            }

            List<Finance_ChargesDetail> waitBanks = new List<ExpressFeechargesModel>
            {
                shopFeechargesModel,eu,at
            }.CopyList<Finance_ChargesDetail>().ToList();
            if (feeChargesList.Count > 0)
            {
                waitBanks.AddRange(feeChargesList.CopyList<Finance_ChargesDetail>().ToList());
            }
            finance_Order_Charges.AttachFee = attache;
            finance_Order_Charges.EUTariff = tax;
            finance_Order_Charges.ShippingFee = shopFee;

            if (t.Bookkeeping)
            {
                finance_Order_Charges.SettlementCompleted = t.Bookkeeping;
                waitBanks.ForEach(x => x.SettlementCompleted = t.Bookkeeping);
            }


            //计算分公司利润
            if (t.IsChargeBranchCompany)
            {
                decimal branchCompanyProfit = attache + tax + shopFee;
                //decimal profit = await CalculateBranchCompanyProfit(branchCompanyProfit, t.BillNo);
                stopwatch.Stop();
                t.timeList.Add("利润计算", stopwatch.ElapsedMilliseconds);
                stopwatch.Restart();
                finance_Order_Charges.Profit = t.CustomerOrderFee - branchCompanyProfit;
            }
            else
            {
                t.CustomerOrderFee = attache + tax + shopFee;
            }

            var result = await Bookkeeping(t, shopFee, tax, attache, waitBanks, t.Bookkeeping, finance_Order_Charges, remark);
            stopwatch.Stop();
            t.timeList.Add("DB数据写入", stopwatch.ElapsedMilliseconds);
            stopwatch.Restart();
            return result;
        }

        private async Task SendKS(string number,
            decimal weight,
            int channelId,
            int customerId,
            int userId,
            decimal profit)
        {
            string periceVersion =
                 (await _unitOfWork.QueryBySqlAsync<string>
                 ($" select PriceVersion from Finance_ChargesDetail(nolock) where BillNo = '{number}' and CustomerId = {customerId}  and ChargeName = '运费(含燃油)'")).FirstOrDefault();
            if (!string.IsNullOrEmpty(periceVersion))
            {
                string notSendKey = @"NotSPVWaring_" + periceVersion + "_" + channelId + "_" + customerId;
                if (!db.KeyExists(notSendKey))
                {

                    var customer = (await _unitOfWork.QueryBySqlAsync<Tuple<string, int>>
                        (@$"select ISNULL(Abbreviation,CompanyName) as Item1,SalesmanUserId as Item2 
                                from User_Customer(nolock) where Id ={customerId}")).FirstOrDefault();

                    string channelName = (await _unitOfWork.QueryBySqlAsync<string>(
                        $@"select  b.Name  from Logistics_Channel(nolock)a join Logistics_Public(nolock)b
                        on a.PublicCode=b.Code
                        where a.Id={channelId}"
                        )).FirstOrDefault();


                    //不包含被忽略的提醒 
                    RestSharp.RestClient client = new RestSharp.RestClient("http://120.24.149.148:9505");
                    RestSharp.RestRequest request = new RestSharp.RestRequest("/WarningNotice/NoticeCard", RestSharp.Method.POST);
                    string noticeText = $"# 报价异常  \n  客户**{customer.Item1}** " +
                    $"  \n  渠道：**{channelName}**  \n  重量：**{weight}**  \n  版本：**{periceVersion}**  \n  **报价低于结算价{0 - profit}元，请尽快确认调整**。";
                    var body = new
                    {
                        ServiceType = "钉钉",
                        ButtonOrient = "0",
                        Text = noticeText,
                        Title = $"报价异常：客户{customer.Item1} 渠道{channelName}",
                        ToAllUser = false,
                        UserIds = new List<int>
                                    {
                                        347,
                                        1,
                                        customer.Item2
                                    },
                        Buttons = new List<dynamic>
                                    {
                                        new
                                        {
                                            Text = "不再提醒该版本报价异常",
                                            Link = $"http://120.24.149.148:9503/api/ExpressFee/NotSend?periceVersion={periceVersion}&channelId={channelId}&customerId={customerId}&access_token=985833DB91DA7B38C3918D3F1F9D2339"
                                        }
                                    }
                    };
                    request.AddJsonBody(body);

                    var restResponse = client.Execute(request);
                    if (restResponse.StatusCode == System.Net.HttpStatusCode.OK)
                    {
                        NotailResult notailResult = null;
                        try
                        {
                            notailResult = Newtonsoft.Json.JsonConvert.DeserializeObject<NotailResult>(restResponse.Content);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("钉钉推送信息反解析失败，请联系立航" + ex.Message);
                        }
                        if (!notailResult.IsSuccess)
                        {
                            throw new Exception("钉钉推送信息api失败，请联系立航" + notailResult.ErrMsg);
                        }
                        //当天不再提醒
                        await db.StringSetAsync(notSendKey, "1", TimeSpan.FromDays(1));
                    }
                    else
                    {
                        throw new Exception("钉钉推送信息http失败，请联系立航" + restResponse.Content);
                    }
                }
            }

        }
        public class NotailResult
        {
            public bool IsSuccess
            {
                get; set;
            }
            public string ErrMsg
            {
                get; set;
            }
        }

        private async Task<decimal> CalculateBranchCompanyProfit(decimal branchCompanyCharge, string systemNo)
        {
            string sql = $@"select * from Finance_Order_Charges(nolock) where Offset=0 and SystemNo = @SystemNo and ChargeTarget=@ChargeTarget";
            var customerCharges = await _unitOfWork.Connection.QueryAsync<Finance_Order_Charges>(sql, new { SystemNo = systemNo, ChargeTarget = ChargeTarget.客户 });
            if (customerCharges?.Any() != true)
            {
                throw new Exception($"找不到订单({systemNo})的客户扣费记录");
            }
            else if (customerCharges.Count() > 1)
            {
                throw new Exception($"存在多条扣费记录");
            }
            else
            {

                var customerCharge = customerCharges.First();
                var customerFeeTotal = customerCharge.ShippingFee + customerCharge.EUTariff + customerCharge.AttachFee;
                var profit = customerFeeTotal - branchCompanyCharge;
                if (profit < 0)
                {
                    //查找查关的运费版本

                    await SendKS(systemNo,
                        customerCharge.Weight, customerCharge.OrderCreateChannelId,
                        customerCharge.CustomerId, 0, profit);
                }
                return profit;
            }
        }

        static string id = "0";
        /// <summary>
        /// 记账
        /// </summary>
        /// <param name="t"></param>
        /// <param name="shopFee"></param>
        /// <param name="tax"></param>
        /// <param name="attache"></param>
        /// <param name="waitBanks"></param>
        /// <returns></returns>
        private async Task<ApiJsonModel> Bookkeeping(ExpressFeechargesModel t,
            decimal shopFee, decimal tax, decimal attache,
            List<Finance_ChargesDetail> waitBanks,
            bool bookkeeping,
            Finance_Order_Charges orderChages, string remark)
        {
            if (waitBanks.Count > 0)
                waitBanks = waitBanks.Where(x => x.SettlementAmount > 0).ToList();
            if (_unitOfWork.Connection.State == System.Data.ConnectionState.Closed)
            {
                _unitOfWork.Connection.Open();
            }
            using (_unitOfWork.Transaction = _unitOfWork.Connection.BeginTransaction())
            {
                try
                {
                    if (!bookkeeping)
                    {
                        //非即时扣费业务，我们清空结算时间。方便生成汇总帐单
                        waitBanks.ForEach(x => x.AccountingDate = null);
                    }

                    System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch();
                    stopwatch.Start();
                    ///帐户计费记录
                    await _unitOfWork.BulkToDBAsync(waitBanks);
                    stopwatch.Stop();
                    t.timeList.Add("帐户计费记录", stopwatch.ElapsedMilliseconds);
                    stopwatch.Restart();

                    ///写入订单计费记录
                    await _unitOfWork.InsertAsync(orderChages);
                    stopwatch.Stop();
                    t.timeList.Add("写入订单计费记录", stopwatch.ElapsedMilliseconds);
                    stopwatch.Restart();

                    if (t.IsChargeBranchCompany)
                    {
                        _unitOfWork.Transaction.Commit();
                        return ApiJsonModel.OK("成功");

                    }

                    //如果包含备注的话，我们认为是需要更新Order_Fee表信息
                    if (!string.IsNullOrEmpty(remark))
                    {
                        await _unitOfWork.ExecuteAsync($"update order_fee set OPCFreight = {shopFee} , AttachFee={attache},UpdateTime=GetDate() ,Remark = '{remark}' where OrderId={t.OrderId}");

                        stopwatch.Stop();
                        t.timeList.Add("更新Order_Fee表", stopwatch.ElapsedMilliseconds);
                        stopwatch.Restart();

                    }

                    //更新运费，失败时回滚所有操作
                    if (bookkeeping && !await _customerAccountService.Deduction(t.CustomerId, shopFee + attache + tax, !t.IsChargeBranchCompany))
                    {
                        stopwatch.Stop();
                        t.timeList.Add("实时扣费", stopwatch.ElapsedMilliseconds);
                        stopwatch.Restart();
                        _unitOfWork.Transaction.Rollback();
                        //更新帐户欠费，防止重复事务，此语句跳过事务处理
                        await _unitOfWork.Connection.ExecuteAsync($"update Finance_Customer_Account set Arrears=1 where customerId={t.CustomerId}");
                        stopwatch.Stop();
                        t.timeList.Add("回滚", stopwatch.ElapsedMilliseconds);
                        stopwatch.Restart();
                        return ApiJsonModel.Error("欠费");
                    }
                    else
                    {
                        _unitOfWork.Transaction.Commit();
                        stopwatch.Stop();
                        t.timeList.Add("结束", stopwatch.ElapsedMilliseconds);
                        stopwatch.Restart();
                        return ApiJsonModel.OK("成功");
                    }
                }
                catch (Exception ex)
                {
                    _unitOfWork.Transaction.Rollback();
                    return ApiJsonModel.Error(ex.Message);
                }
                finally
                {
                    _unitOfWork.Connection.Close();
                    _unitOfWork.Dispose();
                }
            }



        }

        public async Task FixBranchCompanyCharge(List<string> lines)
        {
            string sql = @"select
ChannelId as ExpressId,
OrderCreateChannelId as PublicExpressId,
CountryCode,
GoodsDeclaredVal as GoodsDeclared,
c.BranchCompanyId as CustomerId,
ch.TransactionTime as TransactionTime,
ch.TransactionTime as AccountingDate,
Weight as BillingWeight,
'全程派送运费' as BillType,
SystemNo as BillNo,
'支出' as IncomeOrESxpense,
SettlementCompleted as Bookkeeping ,
TrackingNumber,
c.BranchCompanyId as BranchCompanyCustomerId,
HasIOSS,
ZipCode,
FeeDescription
from Finance_Order_Charges  ch
left join User_Customer c on c.Id=ch.CustomerId
where TrackingNumber in @TrackingNumbers";
            var list = await _unitOfWork.Connection.QueryAsync<ExpressFeechargesModel>(sql, new { TrackingNumbers = lines });
            if (list?.Any() == true)
            {

                foreach (var m in list)
                {
                    System.Threading.Thread.Sleep(300);
                    await SendBranchCompanyChargeMessage(m, m.BranchCompanyCustomerId.Value);
                }
            }
        }

        public async Task AddNotSend(string periceVersion, string channelId, string customerId)
        {
            string notSendKey = @"NotSPVWaring_" + periceVersion + "_" + channelId + "_" + customerId;
            //久不再提醒
            await db.StringSetAsync(notSendKey, "1");
        }

        public async Task ReChargeByTime(ReChargeByTimeParam param)
        {
            if (param == null || param.From == null || param.To == null)
            {
                throw new Exception("参数无效");
            }
            else
            {
                string sql = "select * from Finance_Charge_Log where CreateTime>=@From and CreateTime<=@To and Success=0";
                string keywork = null;
                if (!string.IsNullOrWhiteSpace(param.Keyword))
                {
                    sql += $" and ErrorMessage like '{param.Keyword}%'";
                }
                var logs = await _unitOfWork.Connection.QueryAsync<Finance_Charge_Log>(sql, new { From = param.From, To = param.To, Keywork = keywork }, transaction: _unitOfWork.Transaction);

                if (logs?.Any() == true)
                {
                    await ReChargeByLogs(logs);
                }
            }
        }

        private async Task ReChargeByLogs(IEnumerable<Finance_Charge_Log> logs)
        {
            if (logs?.Any() == true)
            {
                foreach (var log in logs)
                {
                    try
                    {
                        var chargingModel = Newtonsoft.Json.JsonConvert.DeserializeObject<ExpressFeechargesModel>(log.Json);

                        var chargeResult = await Charges(chargingModel);
                        if (chargeResult.success == true)
                        {
                            await _finance_Charge_LogRepository.UpdateByConditionAsync(i => i.Id == log.Id, new { Success = true, ErrorMessage = string.Empty });
                        }
                    }
                    catch (Exception ex)
                    {

                    }
                }
            }
        }
    }
}
