using AutoMapper;
using IRaCIS.Application.Interfaces;
using IRaCIS.Application.Contracts;
using IRaCIS.Application.Contracts.Pay;
using IRaCIS.Core.Domain.Share;
using System.Linq.Expressions;
using IRaCIS.Core.Infra.EFCore;
using IRaCIS.Core.Domain.Models;
using IRaCIS.Core.Infrastructure.Extention;
using Panda.DynamicWebApi.Attributes;

namespace IRaCIS.Application.Services
{
    public class CalculateService :BaseService, ICalculateService
    {
        private readonly IRepository<Payment> _paymentRepository;
        private readonly IRepository<TrialPaymentPrice> _trialPaymentRepository;
        private readonly IRepository<ReviewerPayInformation> _doctorPayInfoRepository;
        private readonly IRepository<Trial> _trialRepository;
        private readonly IRepository<Doctor> _doctorRepository;
        private readonly IRepository<Workload> _doctorWorkloadRepository;
        private readonly IRepository<RankPrice> _rankPriceRepository;
        private readonly IRepository<PaymentDetail> _paymentDetailRepository;
        private readonly IVolumeRewardService _volumeRewardPriceService;
        private readonly IRepository<ExchangeRate> _exchangeRateRepository;
        private readonly IRepository<PaymentAdjustment> _payAdjustmentRepository;
        private readonly IRepository<Enroll> _enrollRepository;

        public CalculateService(IRepository<Payment> paymentRepository, IRepository<TrialPaymentPrice> trialPaymentPriceRepository,
            IRepository<ReviewerPayInformation> reviewerPayInfoRepository,
            IRepository<Trial> trialRepository,
            IRepository<Doctor> doctorRepository,
            IRepository<Workload> workloadRepository,
            IRepository<RankPrice> rankPriceRepository,
            IRepository<PaymentDetail> paymentDetailRepository,
            IVolumeRewardService volumeRewardService,
            IRepository<ExchangeRate> exchangeRateRepository,
            IRepository<Enroll> EnrollRepository,
            IRepository<PaymentAdjustment> paymentAdjustmentRepository)
        {
            _paymentRepository = paymentRepository;
            _trialPaymentRepository = trialPaymentPriceRepository;
            _doctorPayInfoRepository = reviewerPayInfoRepository;
            _trialRepository = trialRepository;
            _doctorRepository = doctorRepository;
            _doctorWorkloadRepository = workloadRepository;
            _rankPriceRepository = rankPriceRepository;
            _paymentDetailRepository = paymentDetailRepository;
            _volumeRewardPriceService = volumeRewardService;
            _exchangeRateRepository = exchangeRateRepository;
            _payAdjustmentRepository = paymentAdjustmentRepository;
            this._enrollRepository = EnrollRepository;
        }

        /// <summary>
        /// 获取某个月下的某些医生最终确认的工作量,用于计算月度费用
        /// </summary>
        private async Task< List<CalculatePaymentDTO>> GetFinalConfirmedWorkloadAndPayPriceList(CalculateDoctorAndMonthDTO calculateFeeParam)
        {
            Expression<Func<Workload, bool>> workloadLambda = x => true;

            DateTime bTime = new DateTime(calculateFeeParam.CalculateMonth.Year, calculateFeeParam.CalculateMonth.Month, 1);
            var eTime = bTime.AddMonths(1);
            workloadLambda = workloadLambda.And(t =>
                t.WorkTime >= bTime && t.WorkTime < eTime);

            workloadLambda = workloadLambda.And(t => calculateFeeParam.NeedCalculateReviewers.Contains(t.DoctorId) && t.DataFrom == (int)WorkLoadFromStatus.FinalConfirm);
            var workLoadQueryable = from doctor in _doctorRepository.AsQueryable()
                                    join workLoad in _doctorWorkloadRepository.Where(workloadLambda) on
                                        doctor.Id equals workLoad.DoctorId
                                    join trial in _trialRepository.AsQueryable() on workLoad.TrialId equals trial.Id
                                    join trialPay in _trialPaymentRepository.AsQueryable() on trial.Id equals trialPay.TrialId
                                    into temp
                                    from trialPay in temp.DefaultIfEmpty()
                                    join doctorPayInfo in _doctorPayInfoRepository.AsQueryable() on doctor.Id equals doctorPayInfo.DoctorId
                                    join rankPrice in _rankPriceRepository.AsQueryable() on doctorPayInfo.RankId equals rankPrice.Id
                                    select new CalculatePaymentDTO()
                                    {
                                        Id = workLoad.Id,
                                        DoctorId = workLoad.DoctorId,
                                        WorkTime = workLoad.WorkTime,
                                        DataFrom = workLoad.DataFrom,
                                        TrialId = workLoad.TrialId,
                                        TrialCode = trial.TrialCode,

                                        Timepoint = workLoad.Timepoint,
                                        TimepointIn24H = workLoad.TimepointIn24H,
                                        TimepointIn48H = workLoad.TimepointIn48H,
                                        Global = workLoad.Global,
                                        Adjudication = workLoad.Adjudication,
                                        AdjudicationIn24H = workLoad.AdjudicationIn24H,
                                        AdjudicationIn48H = workLoad.AdjudicationIn48H,
                                        Training = workLoad.Training,
                                        RefresherTraining = workLoad.RefresherTraining,
                                        Downtime = workLoad.Downtime,

                                        TrialAdditional = trialPay.TrialAdditional,
                                        PersonalAdditional = doctorPayInfo.Additional,
                                        AdjustmentMultiple = trialPay.AdjustmentMultiple,

                                        TimepointPrice = rankPrice.Timepoint,
                                        TimepointIn24HPrice = rankPrice.TimepointIn24H,
                                        TimepointIn48HPrice = rankPrice.TimepointIn48H,
                                        AdjudicationPrice = rankPrice.Adjudication,
                                        AdjudicationIn24HPrice = rankPrice.AdjudicationIn24H,
                                        AdjudicationIn48HPrice = rankPrice.AdjudicationIn48H,
                                        DowntimePrice = rankPrice.Downtime,
                                        GlobalPrice = rankPrice.Global,
                                        TrainingPrice = rankPrice.Training,
                                        RefresherTrainingPrice = rankPrice.RefresherTraining
                                    };

            return await workLoadQueryable.ToListAsync();
        }

        /// <summary>
        /// 计算月度费用,并调用AddOrUpdateMonthlyPayment和AddOrUpdateMonthlyPaymentDetail方法,
        /// 将费用计算的月度数据及详情保存
        /// </summary>
        [NonDynamicMethod]
        public async Task<IResponseOutput> CalculateMonthlyPayment(CalculateDoctorAndMonthDTO param, string token)
        {
            var yearMonth = param.CalculateMonth.ToString("yyyy-MM");
            var rate = await _exchangeRateRepository.FirstOrDefaultAsync(u => u.YearMonth == yearMonth);

            decimal exchangeRate = rate?.Rate ?? 0;

            var workLoadAndPayPriceList = await GetFinalConfirmedWorkloadAndPayPriceList(param);
            var volumeRewardPriceList = await _volumeRewardPriceService.GetVolumeRewardPriceList();

            #region 奖励数据校验
            for (int i = 0; i < volumeRewardPriceList.Count; i++)
            {
                if (i == 0 && volumeRewardPriceList[i].Min != 0)
                {
                //---Volume reward data error.
                    return ResponseOutput.NotOk(_localizer["Cal_VolDataErr"]);
                }
                if (i > 0)
                {
                    if (volumeRewardPriceList[i - 1].Max + 1 != volumeRewardPriceList[i].Min)
                //---Volume reward data error.
                        return ResponseOutput.NotOk(_localizer["Cal_VolDataErr"]);
                }
            }


            #endregion


            List<PaymentModel> paymentList = new List<PaymentModel>();
            List<ReviewerPaymentUSD> reviewerPaymentUSDList = new List<ReviewerPaymentUSD>();

            // 获取所有医生费用   一次从数据库里面全部取出来

            var allDoctorList = workLoadAndPayPriceList.Where(x => param.NeedCalculateReviewers.Contains(x.DoctorId)).ToList();
            var allDoctorIds = allDoctorList.Select(x => x.DoctorId).Distinct().ToList();
            var listTrialId = allDoctorList.Select(x => x.TrialId).Distinct().ToList();

            var trialDoctorlist= await (from enroll in _enrollRepository.Where(x=> listTrialId.Contains(x.TrialId)|| allDoctorIds.Contains(x.DoctorId))
                                  join price in _trialPaymentRepository.Where() on enroll.TrialId equals price.TrialId
                                  select new DoctorPrice()
                                  {
                                      IsNewTrial = price.IsNewTrial,
                                      AdjustmentMultiple = enroll.AdjustmentMultiple,
                                      TrialId=enroll.TrialId,
                                      DoctorId = enroll.DoctorId,
                                      Training=enroll.Training,
                                      Adjudication=enroll.Adjudication, 
                                      Adjudication24H=enroll.Adjudication24H,
                                      Adjudication48H=  enroll.Adjudication48H,
                                      Downtime=enroll.Downtime,
                                      Global=enroll.Global,
                                      RefresherTraining=enroll.RefresherTraining,
                                      Timepoint= enroll.Timepoint,
                                      Timepoint24H=enroll.Timepoint24H,
                                      Timepoint48H=enroll.Timepoint48H,
                                  }).ToListAsync();


            foreach (var doctor in param.NeedCalculateReviewers)
            {
                if (await _paymentRepository.AnyAsync(u => u.DoctorId == doctor && u.YearMonth == yearMonth && u.IsLock))
                {
                    break;
                }
                List<PaymentDetailCommand> paymentDetailList = new List<PaymentDetailCommand>();
                decimal totalNormal = 0;

                //计算单个医生费用统,并且插入到统计表
                var doctorWorkloadAndPayPriceList = workLoadAndPayPriceList.Where(u => u.DoctorId == doctor).ToList();

                //阅片数量 计算奖励费用
                int readCount = 0;

                int codeOrder = 0;

                //这里需要改

                foreach (var item in doctorWorkloadAndPayPriceList)
                {
                    var doctordata = trialDoctorlist.Where(x => x.IsNewTrial ?? false && x.Training == item.Training && x.DoctorId == item.DoctorId).FirstOrDefault();

                    if (doctordata != null)
                    {
                        item.Training = doctordata.Training??0;
                        item.Adjudication = doctordata.Adjudication??0;
                        item.AdjudicationIn24H = doctordata.Adjudication24H??0;
                        item.AdjudicationIn48H = doctordata.Adjudication48H??0;
                        item.Downtime = doctordata.Downtime??0;
                        item.Global = doctordata.Global??0;
                        item.RefresherTraining = doctordata.RefresherTraining??0;
                        item.Timepoint = doctordata.Timepoint??0;
                        item.TimepointIn24H = doctordata.Timepoint24H??0;
                        item.TimepointIn48H = doctordata.Timepoint48H??0;
                        item.PersonalAdditional = 0;
                    }
                    ++codeOrder;
                    readCount += (item.Timepoint + item.TimepointIn24H + item.TimepointIn48H
                    + item.Adjudication + item.AdjudicationIn24H + item.AdjudicationIn48H);
                    decimal trainingTotal = item.Training * item.TrainingPrice;
                    decimal refresherTrainingTotal = item.RefresherTraining * item.RefresherTrainingPrice;
                    decimal downtimeTotal = item.Downtime * item.DowntimePrice;
                    //规则定义  global 的价格是Tp和个人附加的一半
                    decimal globalTotal = item.Global * (item.TimepointPrice / 2 + item.PersonalAdditional / 2);

                    //项目如果没有添加附加数据 默认为0
                    decimal timePointTotal = item.Timepoint * (item.TimepointPrice * item.AdjustmentMultiple + item.PersonalAdditional + (item.TrialAdditional == null ? 0 : (decimal)item.TrialAdditional));

                    decimal timePointIn24HTotal = item.TimepointIn24H * (item.TimepointIn24HPrice * item.AdjustmentMultiple + item.PersonalAdditional);
                    decimal timePointIn48HTotal = item.TimepointIn48H * (item.TimepointIn48HPrice * item.AdjustmentMultiple + item.PersonalAdditional);
                    decimal adjudicationTotal = item.Adjudication * (item.AdjudicationPrice * item.AdjustmentMultiple + item.PersonalAdditional + (item.TrialAdditional == null ? 0 : (decimal)item.TrialAdditional));
                    decimal adjudicationIn24HTotal = item.AdjudicationIn24H * (item.AdjudicationIn24HPrice * item.AdjustmentMultiple + item.PersonalAdditional);
                    decimal adjudicationIn48HTotal = item.AdjudicationIn48H * (item.AdjudicationIn48HPrice * item.AdjustmentMultiple + item.PersonalAdditional);

                    totalNormal += (trainingTotal + refresherTrainingTotal + downtimeTotal + globalTotal + timePointTotal + timePointIn24HTotal
                        + timePointIn48HTotal + adjudicationTotal + adjudicationIn24HTotal + adjudicationIn48HTotal);

                    #region 统计明细信息

                    paymentDetailList.Add(new PaymentDetailCommand
                    {
                        TrialCode = item.TrialCode,
                        PaymentType = "Training",
                        Count = item.Training,
                        BasePrice = item.TrainingPrice,
                        PersonalAdditional = 0,
                        TrialAdditional = 0,
                        PaymentId = Guid.Empty,
                        DoctorId = item.DoctorId,
                        TrialId = item.TrialId,
                        ShowTypeOrder = 1,
                        ShowCodeOrder = codeOrder,
                        ExchangeRate = exchangeRate,
                        YearMonth = yearMonth,
                        PaymentUSD = item.Training * item.TrainingPrice,
                        PaymentCNY = item.Training * item.TrainingPrice * exchangeRate
                    });
                    paymentDetailList.Add(new PaymentDetailCommand
                    {
                        TrialCode = item.TrialCode,
                        PaymentType = "Refresher Training",
                        Count = item.RefresherTraining,
                        BasePrice = item.RefresherTrainingPrice,
                        PersonalAdditional = 0,
                        TrialAdditional = 0,
                        PaymentId = Guid.Empty,
                        DoctorId = item.DoctorId,
                        TrialId = item.TrialId,
                        ShowTypeOrder = 2,
                        ShowCodeOrder = codeOrder,
                        ExchangeRate = exchangeRate,
                        YearMonth = yearMonth,
                        PaymentUSD = item.RefresherTraining * item.RefresherTrainingPrice,
                        PaymentCNY = item.RefresherTraining * item.RefresherTrainingPrice * exchangeRate
                    });
                    paymentDetailList.Add(new PaymentDetailCommand
                    {
                        TrialCode = item.TrialCode,
                        PaymentType = "Downtime",
                        Count = item.Downtime,
                        BasePrice = item.DowntimePrice,
                        PersonalAdditional = 0,
                        TrialAdditional = 0,
                        PaymentId = Guid.Empty,
                        DoctorId = item.DoctorId,
                        TrialId = item.TrialId,
                        ShowTypeOrder = 3,
                        ShowCodeOrder = codeOrder,
                        ExchangeRate = exchangeRate,
                        YearMonth = yearMonth,
                        PaymentUSD = item.Downtime * item.DowntimePrice,
                        PaymentCNY = item.Downtime * item.DowntimePrice * exchangeRate
                    });

                    paymentDetailList.Add(new PaymentDetailCommand
                    {
                        TrialCode = item.TrialCode,
                        PaymentType = "Timepoint Regular",
                        Count = item.Timepoint,
                        BasePrice = item.TimepointPrice,
                        PersonalAdditional = doctordata!=null?0: item.PersonalAdditional,
                        TrialAdditional = doctordata != null ? 0 : item.TimepointPrice * (item.AdjustmentMultiple - 1) + (item.TrialAdditional == null ? 0 : (decimal)item.TrialAdditional),
                        PaymentId = Guid.Empty,
                        DoctorId = item.DoctorId,
                        TrialId = item.TrialId,
                        ShowTypeOrder = 4,
                        ShowCodeOrder = codeOrder,
                        ExchangeRate = exchangeRate,
                        YearMonth = yearMonth,
                        PaymentUSD = item.Timepoint * (item.TimepointPrice * item.AdjustmentMultiple + item.PersonalAdditional + (item.TrialAdditional == null ? 0 : (decimal)item.TrialAdditional)),
                        PaymentCNY = item.Timepoint * (item.TimepointPrice * item.AdjustmentMultiple + item.PersonalAdditional + (item.TrialAdditional == null ? 0 : (decimal)item.TrialAdditional)) * exchangeRate
                    });

                    paymentDetailList.Add(new PaymentDetailCommand
                    {
                        TrialCode = item.TrialCode,
                        PaymentType = "Timepoint 48-Hour",
                        Count = item.TimepointIn48H,
                        BasePrice = item.TimepointIn48HPrice,
                        PersonalAdditional = doctordata != null ? 0 : item.PersonalAdditional,
                        TrialAdditional = doctordata != null ? 0 : item.TimepointIn48HPrice * (item.AdjustmentMultiple - 1) + 0,//48小时不加项目附加
                        PaymentId = Guid.Empty,
                        DoctorId = item.DoctorId,
                        TrialId = item.TrialId,
                        ShowTypeOrder = 5,
                        ShowCodeOrder = codeOrder,
                        ExchangeRate = exchangeRate,
                        YearMonth = yearMonth,
                        PaymentUSD = item.TimepointIn48H * (item.TimepointIn48HPrice * item.AdjustmentMultiple + 0 + item.PersonalAdditional),
                        PaymentCNY = item.TimepointIn48H * (item.TimepointIn48HPrice * item.AdjustmentMultiple + 0 + item.PersonalAdditional) * exchangeRate
                    });
                    paymentDetailList.Add(new PaymentDetailCommand
                    {
                        TrialCode = item.TrialCode,
                        PaymentType = "Timepoint 24-Hour",
                        Count = item.TimepointIn24H,
                        BasePrice = item.TimepointIn24HPrice,
                        PersonalAdditional = doctordata != null ? 0 : item.PersonalAdditional,
                        TrialAdditional = doctordata != null ? 0 : item.TimepointIn24HPrice * (item.AdjustmentMultiple - 1) + 0,
                        PaymentId = Guid.Empty,
                        DoctorId = item.DoctorId,
                        TrialId = item.TrialId,
                        ShowTypeOrder = 6,
                        ShowCodeOrder = codeOrder,
                        ExchangeRate = exchangeRate,
                        YearMonth = yearMonth,
                        PaymentUSD = item.TimepointIn24H * (item.TimepointIn24HPrice * item.AdjustmentMultiple + 0 + item.PersonalAdditional),
                        PaymentCNY = item.TimepointIn24H * (item.TimepointIn24HPrice * item.AdjustmentMultiple + 0 + item.PersonalAdditional) * exchangeRate
                    });
                    paymentDetailList.Add(new PaymentDetailCommand
                    {
                        TrialCode = item.TrialCode,
                        PaymentType = "Adjudication Regular",
                        Count = item.Adjudication,
                        BasePrice = item.AdjudicationPrice,
                        PersonalAdditional = doctordata != null ? 0 : item.PersonalAdditional,
                        TrialAdditional = doctordata != null ? 0 : item.AdjudicationPrice * (item.AdjustmentMultiple - 1) + (item.TrialAdditional == null ? 0 : (decimal)item.TrialAdditional),
                        PaymentId = Guid.Empty,
                        DoctorId = item.DoctorId,
                        TrialId = item.TrialId,
                        ShowTypeOrder = 7,
                        ShowCodeOrder = codeOrder,
                        ExchangeRate = exchangeRate,
                        YearMonth = yearMonth,
                        PaymentUSD = item.Adjudication * (item.AdjudicationPrice * item.AdjustmentMultiple + item.PersonalAdditional + (item.TrialAdditional == null ? 0 : (decimal)item.TrialAdditional)),
                        PaymentCNY = item.Adjudication * (item.AdjudicationPrice * item.AdjustmentMultiple + item.PersonalAdditional + (item.TrialAdditional == null ? 0 : (decimal)item.TrialAdditional)) * exchangeRate
                    });

                    paymentDetailList.Add(new PaymentDetailCommand
                    {
                        TrialCode = item.TrialCode,
                        PaymentType = "Adjudication 48-Hour",
                        Count = item.AdjudicationIn48H,
                        BasePrice = item.AdjudicationIn48HPrice,
                        PersonalAdditional = doctordata != null ? 0 : item.PersonalAdditional,
                        TrialAdditional = doctordata != null ? 0 : item.AdjudicationIn48HPrice * (item.AdjustmentMultiple - 1) + 0,
                        PaymentId = Guid.Empty,
                        DoctorId = item.DoctorId,
                        TrialId = item.TrialId,
                        ShowTypeOrder = 8,
                        ShowCodeOrder = codeOrder,
                        ExchangeRate = exchangeRate,
                        YearMonth = yearMonth,
                        PaymentUSD = item.AdjudicationIn48H * (item.AdjudicationIn48HPrice * item.AdjustmentMultiple + item.PersonalAdditional + 0),
                        PaymentCNY = item.AdjudicationIn48H * (item.AdjudicationIn48HPrice * item.AdjustmentMultiple + item.PersonalAdditional + 0) * exchangeRate
                    });
                    paymentDetailList.Add(new PaymentDetailCommand
                    {
                        TrialCode = item.TrialCode,
                        PaymentType = "Adjudication 24-Hour",
                        Count = item.AdjudicationIn24H,
                        BasePrice = item.AdjudicationIn24HPrice,
                        PersonalAdditional = doctordata != null ? 0 : item.PersonalAdditional,
                        TrialAdditional = doctordata != null ? 0 : item.AdjudicationIn24HPrice * (item.AdjustmentMultiple - 1) + 0,
                        PaymentId = Guid.Empty,
                        DoctorId = item.DoctorId,
                        TrialId = item.TrialId,
                        ShowTypeOrder = 9,
                        ShowCodeOrder = codeOrder,
                        ExchangeRate = exchangeRate,
                        YearMonth = yearMonth,
                        PaymentUSD = item.AdjudicationIn24H * (item.AdjudicationIn24HPrice * item.AdjustmentMultiple + 0 + item.PersonalAdditional),
                        PaymentCNY = item.AdjudicationIn24H * (item.AdjudicationIn24HPrice * item.AdjustmentMultiple + 0 + item.PersonalAdditional) * exchangeRate
                    });
                    paymentDetailList.Add(new PaymentDetailCommand
                    {
                        TrialCode = item.TrialCode,
                        PaymentType = "Global",
                        Count = item.Global,
                        BasePrice = item.TimepointPrice / 2,//item.GlobalPrice,
                        PersonalAdditional = doctordata != null ? 0 : item.PersonalAdditional / 2,
                        TrialAdditional = 0,
                        PaymentId = Guid.Empty,
                        DoctorId = item.DoctorId,
                        TrialId = item.TrialId,
                        ShowTypeOrder = 10,
                        ShowCodeOrder = codeOrder,
                        ExchangeRate = exchangeRate,
                        YearMonth = yearMonth,
                        PaymentUSD = item.Global * (item.TimepointPrice / 2 + item.PersonalAdditional / 2),
                        PaymentCNY = item.Global * (item.TimepointPrice / 2 + item.PersonalAdditional / 2) * exchangeRate
                    });
                    #endregion
                }

                int typeOrder = 0;
                if (readCount > 0)
                {
                    paymentDetailList.Add(new PaymentDetailCommand
                    {
                        TrialCode = "Volume Reward",
                        PaymentType = "Total TP & AD",
                        Count = readCount,
                        BasePrice = 0,
                        PersonalAdditional = 0,
                        TrialAdditional = 0,
                        PaymentId = Guid.Empty,
                        DoctorId = doctor,
                        TrialId = Guid.Empty,
                        ShowTypeOrder = typeOrder,
                        ShowCodeOrder = (++codeOrder),
                        ExchangeRate = exchangeRate,
                        YearMonth = yearMonth,
                        PaymentUSD = 0,
                        PaymentCNY = 0
                    });

                    foreach (var awardItem in volumeRewardPriceList)
                    {
                        ++typeOrder;
                        if ((readCount - awardItem.Min + 1) < 0)
                        {
                            break;
                        }
                        if (awardItem.Min == 0)
                        {
                            paymentDetailList.Add(new PaymentDetailCommand
                            {
                                TrialCode = "Volume Reward",
                                PaymentType = awardItem.Min + "-" + awardItem.Max,
                                Count = readCount >= awardItem.Max ?
                            (awardItem.Max - awardItem.Min) : (readCount - awardItem.Min),
                                BasePrice = awardItem.Price,
                                PersonalAdditional = 0,
                                TrialAdditional = 0,
                                PaymentId = Guid.Empty,//result.Data,
                                DoctorId = doctor,
                                TrialId = Guid.Empty,
                                ShowTypeOrder = typeOrder,
                                ShowCodeOrder = (++codeOrder),
                                ExchangeRate = exchangeRate,
                                YearMonth = yearMonth,
                                PaymentUSD = (readCount >= awardItem.Max ?
                            (awardItem.Max - awardItem.Min) : (readCount - awardItem.Min)) * awardItem.Price,
                                PaymentCNY = (readCount >= awardItem.Max ?
                            (awardItem.Max - awardItem.Min) : (readCount - awardItem.Min)) * awardItem.Price * exchangeRate
                            });
                        }
                        else
                        {
                            paymentDetailList.Add(new PaymentDetailCommand
                            {
                                TrialCode = "Volume Reward",
                                PaymentType = awardItem.Min + "-" + awardItem.Max,
                                Count = readCount >= awardItem.Max ?
                                (awardItem.Max - awardItem.Min + 1) : (readCount - awardItem.Min + 1),
                                BasePrice = awardItem.Price,
                                PersonalAdditional = 0,
                                TrialAdditional = 0,
                                PaymentId = Guid.Empty,
                                DoctorId = doctor,
                                TrialId = Guid.Empty,
                                ShowTypeOrder = typeOrder,
                                ShowCodeOrder = (++codeOrder),
                                ExchangeRate = exchangeRate,
                                YearMonth = yearMonth,
                                PaymentUSD = (readCount >= awardItem.Max ?
                                (awardItem.Max - awardItem.Min + 1) : (readCount - awardItem.Min + 1)) * awardItem.Price,
                                PaymentCNY = (readCount >= awardItem.Max ?
                                (awardItem.Max - awardItem.Min + 1) : (readCount - awardItem.Min + 1)) * awardItem.Price * exchangeRate
                            });
                        }
                    }

                }
                decimal award = 0;
                volumeRewardPriceList = volumeRewardPriceList.OrderBy(u => u.Min).ToList();

                var levelTemp = -1; //用来计算属于哪一个挡位
                foreach (var awarPriceitem in volumeRewardPriceList)
                {
                    if (awarPriceitem.Min == 0)
                    {
                        if (readCount > awarPriceitem.Max)
                        {
                            ++levelTemp;
                            award += (awarPriceitem.Max - awarPriceitem.Min) * awarPriceitem.Price;
                        }
                        if (awarPriceitem.Min < readCount && readCount < awarPriceitem.Max)
                        {
                            ++levelTemp;
                            award += (readCount - awarPriceitem.Min) * awarPriceitem.Price;
                            break; ;
                        }
                    }
                    else
                    {
                        if (readCount > awarPriceitem.Max)
                        {
                            ++levelTemp;
                            award += (awarPriceitem.Max - awarPriceitem.Min + 1) * awarPriceitem.Price;
                        }
                        if (awarPriceitem.Min < readCount && readCount < awarPriceitem.Max)
                        {
                            ++levelTemp;
                            award += (readCount - awarPriceitem.Min + 1) * awarPriceitem.Price;
                            break; ;
                        }
                    }
                }
                decimal totalUSD = award + totalNormal;//总费用

                var result = await AddOrUpdateMonthlyPayment(new PaymentCommand
                {
                    DoctorId = doctor,
                    Year = param.CalculateMonth.Year,
                    Month = param.CalculateMonth.Month,
                    PaymentUSD = totalUSD,
                    CalculateUser = token,
                    CalculateTime = DateTime.Now,
                    ExchangeRate = exchangeRate,
                    PaymentCNY = exchangeRate * totalUSD,
                });


                reviewerPaymentUSDList.Add(new ReviewerPaymentUSD { DoctorId = doctor, PaymentUSD = totalUSD, RecordId = result.Data });
                foreach (var detail in paymentDetailList)
                {
                    //var data = trialDoctorlist.FirstOrDefault(x => x.DoctorId == detail.DoctorId && x.TrialId == detail.TrialId && x.IsNewTrial == true && (x.AdjustmentMultiple??0) != 0);
                    //if (data != null)
                    //{
                    //    detail.BasePrice = data.AdjustmentMultiple??0;
                    //    detail.PersonalAdditional = 0;
                    //    detail.TrialAdditional = 0;
                    //}
                        
                    detail.PaymentId = result.Data;
                }
                await AddOrUpdateMonthlyPaymentDetail(paymentDetailList, result.Data);

                await UpdatePaymentAdjustment(doctor, yearMonth);
            }
            return ResponseOutput.Ok(reviewerPaymentUSDList);
        }




        // 重新计算调整费用

        private async Task UpdatePaymentAdjustment(Guid reviewerId, string yearMonth)
        {
            var adjustList = await _payAdjustmentRepository.Where(u => u.YearMonth == yearMonth &&
                                                                          !u.IsLock && u.ReviewerId == reviewerId).ToListAsync();


            var needUpdatePayment = adjustList.GroupBy(t => t.ReviewerId).Select(g => new
            {
                ReviewerId = g.Key,
                AdjustCNY = g.Sum(t => t.AdjustmentCNY),
                AdjustUSD = g.Sum(t => t.AdjustmentUSD)
            });

            foreach (var reviewer in needUpdatePayment)
            {
               await  _paymentRepository.BatchUpdateNoTrackingAsync(u => u.YearMonth == yearMonth &&
                                               !u.IsLock && u.DoctorId == reviewer.ReviewerId, t => new Payment()
                                               {
                                                   AdjustmentUSD = reviewer.AdjustUSD,
                                                   AdjustmentCNY = reviewer.AdjustCNY

                                               });
            }
        }

        /// <summary>
        /// 保存费用计算的月度数据
        /// </summary>
        private async Task<IResponseOutput<Guid>> AddOrUpdateMonthlyPayment(PaymentCommand addOrUpdateModel)
        {
            var success = false;
            var paymentModel = await _paymentRepository.FirstOrDefaultAsync(t =>
                t.DoctorId == addOrUpdateModel.DoctorId && t.YearMonth == addOrUpdateModel.YearMonth);



            //var taxCNY = GetTax(addOrUpdateModel.PaymentCNY);
            //var actuallyPaidCNY = addOrUpdateModel.PaymentCNY - taxCNY;
            //var bankTransferCNY = addOrUpdateModel.PaymentCNY - taxCNY;

            if (paymentModel == null)
            {
                var payment = _mapper.Map<Payment>(addOrUpdateModel);

                //payment.BankTransferCNY = bankTransferCNY;
                //payment.TaxCNY= taxCNY;
                //payment.BankTransferCNY = bankTransferCNY;
                payment.YearMonthDate = DateTime.Parse(payment.YearMonth);

                payment =await _paymentRepository.AddAsync(payment);
                success =await _paymentRepository.SaveChangesAsync();
                return ResponseOutput.Result(success, payment.Id);
            }
            else
            {
                // 如果是 当月计算的工作量费用 和 调整费用都为0,则删除该行记录
                if (addOrUpdateModel.PaymentUSD == 0 && paymentModel.AdjustmentUSD == 0)
                {
                    success =await _paymentRepository.BatchDeleteNoTrackingAsync(u => u.Id == paymentModel.Id);
                    //_paymentDetailRepository.Delete(u=>u.PaymentId==paymentModel.Id);
                }
                else
                {
                    success = await _paymentRepository.BatchUpdateNoTrackingAsync(t => t.Id == paymentModel.Id, u => new Payment()
                    {
                        PaymentUSD = addOrUpdateModel.PaymentUSD,
                        CalculateTime = addOrUpdateModel.CalculateTime,
                        CalculateUser = addOrUpdateModel.CalculateUser,
                        //TaxCNY = taxCNY,
                        //ActuallyPaidCNY = actuallyPaidCNY,
                        //BankTransferCNY = bankTransferCNY,
                        PaymentCNY = addOrUpdateModel.PaymentCNY,
                        ExchangeRate = addOrUpdateModel.ExchangeRate
                    });
                }

                return ResponseOutput.Result(success, paymentModel.Id);
            }

        }




        /// <summary>
        /// 保存费用计算的月度详情
        /// </summary>
        private async Task<bool> AddOrUpdateMonthlyPaymentDetail(List<PaymentDetailCommand> addOrUpdateList, Guid paymentId)
        {
            //var paymentDetailIds = addOrUpdateList.Select(t => t.PaymentId).ToList();
            await _paymentDetailRepository.BatchDeleteNoTrackingAsync(t => t.PaymentId == paymentId);
            await _paymentDetailRepository.AddRangeAsync(_mapper.Map<List<PaymentDetail>>(addOrUpdateList));
            return await _paymentDetailRepository.SaveChangesAsync();
        }



        /// <summary>
        /// 获取待计算费用的Reviewer对应的月份列表
        /// </summary>
        public async Task<List<CalculateNeededDTO>> GetNeedCalculateReviewerList(Guid reviewerId, string yearMonth)
        {
            Expression<Func<Payment, bool>> calculateLambda = u => !u.IsLock;
            if (reviewerId != Guid.Empty)
            {
                calculateLambda = calculateLambda.And(u => u.DoctorId == reviewerId);
            }
            if (!string.IsNullOrWhiteSpace(yearMonth))
            {
                calculateLambda = calculateLambda.And(u => u.YearMonth == yearMonth);
            }
            return await _paymentRepository.Where(calculateLambda).ProjectTo<CalculateNeededDTO>(_mapper.ConfigurationProvider).ToListAsync();
        }

        /// <summary>
        /// 查询Reviewer某个月的费用是否被锁定
        /// </summary>
        public async Task<bool> IsLock(Guid reviewerId, string yearMonth)
        {
            return await _paymentRepository.AnyAsync(u => u.DoctorId == reviewerId && u.YearMonth == yearMonth && u.IsLock);
        }

        //public bool ResetMonthlyPayment(Guid reviewerId, Guid trialId, string yearMonth)
        //{
        //    var payment = _paymentRepository.FindSingleOrDefault(u => u.DoctorId == reviewerId && u.YearMonth == yearMonth);
        //    payment.PaymentCNY = 0;
        //    payment.PaymentUSD = 0;
        //    _paymentRepository.Update(payment);
        //    _paymentDetailRepository.Delete(u=>u.DoctorId==reviewerId && u.TrialId==trial)
        //}
    }
}