using JiaZhiQuan.Common.Models.MallGoodsModel;
using JiaZhiQuan.Common.Models.VO.UE;
using System.Collections.Generic;
using System.Threading.Tasks;
using Wicture.DbRESTFul;
using Wicture.DbRESTFul.Infrastructure.Repository;
using static JiaZhiQuan.Common.Models.CommonConst;
using System.Linq;
using System;
using System.Numerics;

namespace JiaZhiQuan.Common {
    public partial class RepositoryExtension {

        #region 计算服务费
        /// <summary>
        /// 获取服务费以及到手价
        /// </summary>
        /// <param name="catId">类目id</param>
        /// <param name="salePrices">多个销售价</param>
        /// <param name="saleType">1，寄售，2挂售</param>
        /// <returns></returns>
        /// <exception cref="LogicalException"></exception>
        public static async Task<Dictionary<int, FeeInfo>> GetSalePriceByCatId(this DbRESTFulRepository repository,
            int serviceFeeTempId, List<int> salePrices, bool careNetAmount = true) {
            if (serviceFeeTempId == 0)
                throw new LogicalException("请输入正确的服务费模板id");

            List<MallServiceFee> fees = new List<MallServiceFee>();
            var feeRange = await repository.QueryAsync<MallServiceFeeRange>(@"SELECT 
                                                        f.`name`,     
                                                        fr.serviceFeeId,
                                                        fr.chargeType,
                                                        fr.startRange,
                                                        fr.endRange,
                                                        fr.chargeAmount,
                                                        fr.chargeRate,
                                                        fr.minFee,
                                                        fr.maxFee,
                                                        tl.displayOrder
                                                       FROM mall_servicefee_temp t 
                                                       INNER JOIN mall_servicefee_temp_list tl on t.id=tl.serviceFeeTempId
                                                       INNER JOIN mall_service_fee f on f.id=tl.serviceFeeId
                                                       INNER JOIN mall_service_fee_range fr on f.id=fr.serviceFeeId
                                                       WHERE  t.id=@serviceFeeTempId AND t.state=1
                                                       ORDER BY fr.startRange", new { serviceFeeTempId });
            var result = new Dictionary<int, FeeInfo>();
            salePrices.ForEach(p => {
                var netFee = CalNetFee(feeRange, p);
                var rst = new FeeInfo {
                    totalServicePrice = (int)(netFee.totalServiceFee * UNIT),
                    totalServiceFee = "-￥" + netFee.totalServiceFee.ToString("0.##"),
                    netAmount = (int)(netFee.netAmount * UNIT),
                    netAmountStr = "￥" + netFee.netAmount.ToString("0.##"), //到手金额
                    fees = netFee.fees ?? new List<MallServiceFee>(), //服务费详情列表
                    isBelowMiniPrice = netFee.isBelowMiniPrice,
                    belowMiniPriceDesc = netFee.isBelowMiniPrice ? $"不足￥{UE_MIN_PRICE}，请重新出价" : ""
                };
                result[p] = rst;
                //服务费也不能大于salePrice
                if (rst.totalServicePrice >= p || rst.netAmount <= 0) {
                    if (!careNetAmount) {
                        //rst.totalServicePrice = p;
                        rst.netAmount = 0;
                        rst.netAmountStr = "￥" + rst.netAmount.ToString("0.##");
                        rst.belowMiniPriceDesc = $"￥0,请重新出价";
                        return;
                    }
                    throw new Exception("商品定价存在问题，服务价过高或到手价为0");
                }
            });
            return result;
        }

        /// <summary>
        /// 获取服务费以及到手价
        /// 
        /// IOS更新，计算到手价和售价
        /// </summary>
        /// <param name="catId">类目id</param>
        /// <param name="salePrices">多个销售价</param>
        /// <param name="saleType">1，寄售，2挂售</param>
        /// <returns></returns>
        /// <exception cref="LogicalException"></exception>
        public static async Task<Dictionary<int, FeeInfo>> GetSalePriceByCatIdV1(this DbRESTFulRepository repository,
            int serviceFeeTempId, List<int> salePrices, bool careNetAmount = true)
        {
            if (serviceFeeTempId == 0)
                throw new LogicalException("请输入正确的服务费模板id");

            List<MallServiceFee> fees = new List<MallServiceFee>();
            var feeRange = await repository.QueryAsync<MallServiceFeeRange>(@"SELECT 
                                                        f.`name`,     
                                                        fr.serviceFeeId,
                                                        fr.chargeType,
                                                        fr.startRange,
                                                        fr.endRange,
                                                        fr.chargeAmount,
                                                        fr.chargeRate,
                                                        fr.minFee,
                                                        fr.maxFee,
                                                        tl.displayOrder
                                                       FROM mall_servicefee_temp t 
                                                       INNER JOIN mall_servicefee_temp_list tl on t.id=tl.serviceFeeTempId
                                                       INNER JOIN mall_service_fee f on f.id=tl.serviceFeeId
                                                       INNER JOIN mall_service_fee_range fr on f.id=fr.serviceFeeId
                                                       WHERE  t.id=@serviceFeeTempId AND t.state=1
                                                       ORDER BY fr.startRange", new { serviceFeeTempId });
            var result = new Dictionary<int, FeeInfo>();
            salePrices.ForEach(p => {
                var netFee = CalNetFeeV1(feeRange, p);
                var rst = new FeeInfo
                {
                    totalServicePrice = (int)(netFee.totalServiceFee * UNIT),
                    totalServiceFee = "-￥" + netFee.totalServiceFee.ToString("0.##"),
                    netAmount = (int)(netFee.netAmount * UNIT),
                    netAmountStr = "￥" + netFee.netAmount.ToString("0.##"), //到手金额
                    fees = netFee.fees ?? new List<MallServiceFee>(), //服务费详情列表
                    isBelowMiniPrice = netFee.isBelowMiniPrice,
                    belowMiniPriceDesc = netFee.isBelowMiniPrice ? $"不足￥{UE_MIN_PRICE}，请重新出价" : ""
                };
                result[p] = rst;
                //服务费也不能大于salePrice
                if (rst.totalServicePrice >= p || rst.netAmount <= 0)
                {
                    if (!careNetAmount)
                    {
                        //rst.totalServicePrice = p;
                        rst.netAmount = 0;
                        rst.netAmountStr = "￥" + rst.netAmount.ToString("0.##");
                        rst.belowMiniPriceDesc = $"￥0,请重新出价";
                        return;
                    }
                    throw new Exception("商品定价存在问题，服务价过高或到手价为0");
                }
            });
            return result;
        }

        /// <summary>
        /// 计算服务费
        /// </summary>
        /// <param name="feeRange"></param>
        /// <param name="salePrice">单位是分</param>
        /// <returns></returns>
        private static NetServiceFee CalNetFee(IEnumerable<MallServiceFeeRange> feeRange, int salePrice) {
            List<MallServiceFee> fees = new List<MallServiceFee>();
            //到手金额
            int netAmount = salePrice;
            int totalServiceFee = 0;
            var feeDic = feeRange.GroupBy(f => f.serviceFeeId).ToDictionary(g => g.Key, g => g.ToList());

            foreach (var item in feeDic) {
                int money = 0;
                string name = string.Empty;
                foreach (var fee in item.Value) {
                    //，1，比例，2，定额
                    switch (fee.chargeType) {
                        case 1:
                            long lmoney = (long)salePrice * fee.chargeRate;
                            int deduction = (int)(lmoney / 10000);
                            name = fee.name + $"({CommonUtils.DivideAndRound(fee.chargeRate, UNIT):0.##}%";
                            if (fee.minFee.HasValue && deduction < fee.minFee.Value) {
                                deduction = fee.minFee.Value;
                                name += $"，最低￥{CommonUtils.DivideAndRound(deduction, UNIT):0.##}";
                            }
                            if (fee.maxFee.HasValue && deduction > fee.maxFee.Value) {
                                deduction = fee.maxFee.Value;
                                name += $"，最高￥{CommonUtils.DivideAndRound(deduction, UNIT):0.##}";
                            }

                            money = deduction;
                            name += ")";
                            break;
                        case 2:
                            money = fee.chargeAmount;
                            name = fee.name + $"(￥{CommonUtils.DivideAndRound(money, UNIT):0.##})";
                            break;
                    }

                    fees.Add(new MallServiceFee {
                        name = name,
                        fee = "-￥" + CommonUtils.DivideAndRound(money, UNIT).ToString("0.##"),
                        displayOrder = fee.displayOrder,
                    });
                    netAmount -= money;
                    totalServiceFee += money;
                }
            }
            fees = fees.OrderByDescending(f => f.displayOrder).ToList();

            return new NetServiceFee {
                fees = fees,
                isBelowMiniPrice = netAmount < UE_MIN_PRICE * UNIT,
                netAmount = CommonUtils.DivideAndRound(netAmount, UNIT),
                totalServiceFee = CommonUtils.DivideAndRound(totalServiceFee, UNIT)
            };
        }

        /// <summary>
        /// 计算服务费
        /// 
        /// IOS更新，转换到手价和售价
        /// </summary>
        /// <param name="feeRange"></param>
        /// <param name="salePrice">单位是分</param>
        /// <returns></returns>
        private static NetServiceFee CalNetFeeV1(IEnumerable<MallServiceFeeRange> feeRange, int salePrice)
        {
            List<MallServiceFee> fees = new List<MallServiceFee>();
            //到手金额
            int netAmount = salePrice;
            int totalServiceFee = 0;
            var feeDic = feeRange.GroupBy(f => f.serviceFeeId).ToDictionary(g => g.Key, g => g.ToList());

            foreach (var item in feeDic)
            {
                int money = 0;
                string name = string.Empty;
                foreach (var fee in item.Value)
                {
                    //，1，比例，2，定额
                    switch (fee.chargeType)
                    {
                        case 1:
                            long lmoney = (long)salePrice * fee.chargeRate;
                            int deduction = (int)(lmoney / 10000);
                            name = fee.name + $"({CommonUtils.DivideAndRound(fee.chargeRate, UNIT):0.##}%";
                            if (fee.minFee.HasValue && deduction < fee.minFee.Value)
                            {
                                deduction = fee.minFee.Value;
                                name += $"，最低￥{CommonUtils.DivideAndRound(deduction, UNIT):0.##}";
                            }
                            if (fee.maxFee.HasValue && deduction > fee.maxFee.Value)
                            {
                                deduction = fee.maxFee.Value;
                                name += $"，最高￥{CommonUtils.DivideAndRound(deduction, UNIT):0.##}";
                            }

                            money = deduction;
                            name += ")";
                            break;
                        case 2:
                            money = fee.chargeAmount;
                            name = fee.name + $"(￥{CommonUtils.DivideAndRound(money, UNIT):0.##})";
                            break;
                    }

                    fees.Add(new MallServiceFee
                    {
                        name = name,
                        fee = "-￥" + CommonUtils.DivideAndRound(money, UNIT).ToString("0.##"),
                        displayOrder = fee.displayOrder,
                    });
                    netAmount += money;
                    totalServiceFee += money;
                }
            }
            fees = fees.OrderByDescending(f => f.displayOrder).ToList();

            return new NetServiceFee
            {
                fees = fees,
                isBelowMiniPrice = netAmount < UE_MIN_PRICE * UNIT,
                netAmount = CommonUtils.DivideAndRound(netAmount, UNIT),
                totalServiceFee = CommonUtils.DivideAndRound(totalServiceFee, UNIT)
            };
        }

        #endregion

        #region 欧拉币抵扣计算
        /// <summary>
        /// 计算价值币抵扣金额
        /// </summary>
        /// <param name="planId">方案id</param>
        /// <param name="salePrice">售价</param>
        /// <returns></returns>
        public static async Task<int> CalPointsDeduct(this DbRESTFulRepository repository, int planId, int salePrice) {
            if (planId <= 0) return 0;
            // 商品价值币抵扣方案 
            var pointsPlans = await repository.QueryAsync<dynamic>($@"SELECT 
                                                           planRangeId,planId,chargeType,startPrice,deductionFee,
                                                            deductionRate,minDeductionFee,maxDeductionFee
                                                           FROM mall_points_plan_range WHERE planId = @planId", new { planId });
            if (pointsPlans == null || pointsPlans.Count() == 0) return 0;
            //配置要保证每个方案只有一种chargeType。
            int rst = 0;
            var pointsPlan = pointsPlans.First();
            switch ((int)pointsPlan.chargeType) {
                case 1://比率
                    rst = (int)(salePrice * pointsPlan.deductionRate / 10000d);
                    // 限制最小抵扣金额
                    if (rst < pointsPlan.minDeductionFee) {
                        rst = pointsPlan.minDeductionFee;
                    }
                    // 限制最大抵扣金额
                    if (rst > pointsPlan.maxDeductionFee && pointsPlan.maxDeductionFee > 0) {
                        rst = pointsPlan.maxDeductionFee;
                    }
                    break;
                case 2://满减
                    if (salePrice >= pointsPlan.startPrice) {
                        rst = pointsPlan.deductionFee;
                    }
                    break;
            }
            //此时抵扣价格应该算多少？是为0还是为salePrice
            rst = rst >= salePrice ? salePrice : rst;
            return rst;
        }
        #endregion

        #region 计算保证金
        public static async Task<int> CalDepositFee(this DbRESTFulRepository repository,
            int pickDepositId, int salePrice) {
            if (pickDepositId <= 0) return 0;
            // 商品价值币抵扣方案 
            var temp = await repository.QueryAsync<dynamic>(
                $@"SELECT depositRangeId,pickDepositId,chargeType,startRange,
                endRange,depositFee, depositRate,mindepositFee,maxdepositFee
                FROM ue_pick_deposit_range WHERE pickDepositId = @pickDepositId",
                new { pickDepositId });
            if (temp == null || temp.Count() == 0) return 0;
            //配置要保证每个方案只有一种chargeType。
            int rst = 0;
            var plan = temp.First();
            if (1 == (int)plan.chargeType) {
                rst = (int)(salePrice * plan.depositRate / 10000d);
                // 限制最小抵扣金额
                if (rst < plan.mindepositFee) {
                    rst = plan.mindepositFee;
                }
                // 限制最大抵扣金额
                if (rst > plan.maxdepositFee && plan.maxdepositFee > 0) {
                    rst = plan.maxdepositFee;
                }
            } else if (2 == (int)plan.chargeType) {
                temp.ForEach(p => {
                    if (p.endRange == null || (int)p.endRange == -1) {
                        p.endRange = (object)int.MaxValue;
                    }
                });
                var deposit = temp.FirstOrDefault(p => (int)p.startRange <= salePrice && (int)p.endRange >= salePrice);
                rst = deposit?.depositFee ?? 0;
            }

            //此时抵扣价格应该算多少？是为0还是为salePrice
            rst = rst >= salePrice ? salePrice : rst;
            return rst;
        }
        #endregion

        #region 计算仓储费

        //计算取回费用 单位分
        /// <summary>
        /// 当商品上架时，要计算lastStorageTime，通过判断saleOrderCloseAt是否有值，
        /// 如果有值，将payAt-shelveAt的天数作为lastStorageTime的值。
        /// 并且要看saleOrderCloseAt和重新上架时间是否同一天。
        /// 如是，则lastStorageTime的值不向上取整，如果不是则向上取整。
        /// </summary>
        /// <param name="order"></param>
        /// <returns></returns>
        public static (int getbackFee, int getbackDays) CalGetBackFee(
            this DbRESTFulRepository repository, UEPostOrder order) {
            //计算天数
            if (order.shelveAt == null) return (0, 0);
            var dDays = 0d;
            if (order.getbackAt == null && order.payAt == null) {
                dDays = (DateTime.Now - order.shelveAt).Value.TotalDays + (double)order.lastStorageTime;
            } else if (order.getbackAt == null && order.payAt.HasValue) {
                dDays = (order.payAt - order.shelveAt).Value.TotalDays + (double)order.lastStorageTime;
            } else if (order.getbackAt.HasValue && order.payAt == null) {
                dDays = (order.getbackAt - order.shelveAt).Value.TotalDays;
            } else {
                dDays = (order.getbackAt - order.saleOrderCloseAt).Value.TotalDays + (double)order.lastStorageTime;
            }
            var getbackDays = (int)Math.Ceiling(dDays);
            var getbackFee = getbackDays - order.upDays <= 0 ? 0 :
                (getbackDays - order.upDays) * order.unitStorageFee;
            return (getbackFee, getbackDays);
        }
        #endregion
        public class FeeInfo {
            public int totalServicePrice { get; set; }
            public string totalServiceFee { get; set; }
            public int netAmount { get; set; }
            public string netAmountStr { get; set; }
            public List<MallServiceFee> fees { get; set; }
            public bool isBelowMiniPrice { get; set; }
            public string belowMiniPriceDesc { get; set; }
        }
        public class UEPickDepositRange {
            /// <summary>
            /// 主键ID
            /// </summary>
            /// <remarks>不可为空，是主键</remarks>
            public int DepositRangeId { get; set; }

            /// <summary>
            /// 方案id
            /// </summary>
            /// <remarks>不可为空</remarks>
            public int PickDepositId { get; set; }

            /// <summary>
            /// 收费方式（1，售价百分比，2，售价区间定额）
            /// </summary>
            /// <remarks>不可为空</remarks>
            public byte ChargeType { get; set; }

            /// <summary>
            /// 下区间价格（单位分）
            /// </summary>
            public int StartRange { get; set; }

            /// <summary>
            /// 上区间价格（单位分）-1表示无限制
            /// </summary>
            public int EndRange { get; set; }

            /// <summary>
            /// 保证金（单位分）
            /// </summary>
            public int DepositFee { get; set; }

            /// <summary>
            /// 保证金比率(万分比)
            /// </summary>
            public decimal DepositRate { get; set; }

            /// <summary>
            /// 最低保证金（单位分）
            /// </summary>
            public int MinDepositFee { get; set; }

            /// <summary>
            /// 最高保证金（单位分）-1表示无限制
            /// </summary>
            public int MaxDepositFee { get; set; }

            /// <summary>
            /// 创建时间-录入时间
            /// </summary>
            /// <remarks>不可为空</remarks>
            public DateTime CreateAt { get; set; }

            /// <summary>
            /// 修改时间
            /// </summary>
            public DateTime? UpdateAt { get; set; }
        }

    }
}
