using AutoMapper;
using Performance.DtoModels;
using Performance.EntityModels;
using Performance.Infrastructure;
using Performance.Repository;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Performance.Services.AllotCompute
{
    /// <summary>
    /// 最终绩效计算
    /// </summary>
    public class ResultComputeService : IAutoInjection
    {
        private readonly BaiscNormService baiscNormService;
        private readonly ComputeDirector computeDirector;
        private readonly BudgetService budgetService;
        private readonly PerforImemployeeRepository perforImEmployeeRepository;
        private readonly PerforRescomputeRepository perforRescomputeRepository;
        private readonly PerforResbaiscnormRepository perforResbaiscnormRepository;
        private readonly PerforResspecialunitRepository perforResspecialunitRepository;
        private readonly PerforImaccountbasicRepository perforImaccountbasicRepository;
        private readonly LogManageService logManageService;
        private readonly PerforImemployeeclinicRepository perforImemployeeclinicRepository;
        private readonly PerforImemployeelogisticsRepository perforImemployeelogisticsRepository;

        public ResultComputeService(
            PerforImemployeeRepository perforImEmployeeRepository,
            PerforRescomputeRepository perforRescomputeRepository,
            PerforResbaiscnormRepository perforResbaiscnormRepository,
            PerforResspecialunitRepository perforResspecialunitRepository,
            PerforImaccountbasicRepository perforImaccountbasicRepository,
            BaiscNormService baiscNormService, ComputeDirector computeDirector,
            LogManageService logManageService, BudgetService budgetService,
            PerforImemployeeclinicRepository perforImemployeeclinicRepository,
            PerforImemployeelogisticsRepository perforImemployeelogisticsRepository)
        {
            this.baiscNormService = baiscNormService;
            this.computeDirector = computeDirector;
            this.perforImEmployeeRepository = perforImEmployeeRepository;
            this.perforRescomputeRepository = perforRescomputeRepository;
            this.perforResbaiscnormRepository = perforResbaiscnormRepository;
            this.perforResspecialunitRepository = perforResspecialunitRepository;
            this.perforImaccountbasicRepository = perforImaccountbasicRepository;
            this.logManageService = logManageService;
            this.budgetService = budgetService;
            this.perforImemployeeclinicRepository = perforImemployeeclinicRepository;
            this.perforImemployeelogisticsRepository = perforImemployeelogisticsRepository;
        }

        /// <summary>
        /// 计算最终数据
        /// </summary>
        /// <param name="excel"></param>
        public List<res_baiscnorm> Compute(per_allot allot, PerExcel excel, List<PerSheet> accountSheet,
            IEnumerable<EmpolyeeTotal> employeeExtra)
        {
            //取出人员信息           
            var empolyeeList = perforImemployeeclinicRepository.GetEntities(t => t.AllotID == allot.ID);
            var accountbasicList = perforImaccountbasicRepository.GetEntities(t => t.AllotID == allot.ID);

            List<ComputeEmployee> computeEmployees = Mapper.Map<List<ComputeEmployee>>(empolyeeList);

            logManageService.WriteMsg("正在生成绩效", "科室主任、护士长 最终绩效数据计算", 1, allot.ID, "ReceiveMessage");
            var computResult = computeDirector.Compute(computeEmployees, accountSheet, allot, employeeExtra);
            //计算 绩效标准 基数（科主任、副主任、护士长 =>> 平均值）
            List<res_baiscnorm> baiscnormList = new List<res_baiscnorm>();
            baiscNormService.ComputeAvg(baiscnormList, accountbasicList, computResult);
            baiscNormService.DocterNurseBaiscnorm(baiscnormList, accountbasicList, accountSheet);

            var empolyeeList2 = perforImEmployeeRepository.GetEntities(t => t.AllotID == allot.ID);
            var computeEmployees2 = Mapper.Map<List<ComputeEmployee>>(empolyeeList2);
            logManageService.WriteMsg("正在生成绩效", "行政中高层 最终绩效数据计算", 1, allot.ID, "ReceiveMessage");
            var computResult2 = computeDirector.Compute(computeEmployees2, allot, baiscnormList, employeeExtra);

            var empolyeeList3 = perforImemployeelogisticsRepository.GetEntities(t => t.AllotID == allot.ID);
            var computeEmployees3 = Mapper.Map<List<ComputeEmployee>>(empolyeeList3);
            logManageService.WriteMsg("正在生成绩效", "行政后勤 最终绩效数据计算", 1, allot.ID, "ReceiveMessage");
            var computResult3 = computeDirector.Compute(computeEmployees3, allot, baiscnormList, employeeExtra);

            //计算 行政中高层 平均值
            baiscNormService.ComputeOtherAvg(baiscnormList, computResult2, empolyeeList2);
            //计算 行政人员 平均值
            baiscNormService.ComputeOtherAvg(baiscnormList, computResult3, empolyeeList3);

            var computes = Mapper.Map<List<res_compute>>(computResult);
            computes.AddRange(Mapper.Map<List<res_compute>>(computResult2));
            computes.AddRange(Mapper.Map<List<res_compute>>(computResult3));
            computes.ForEach(t => t.AllotID = allot.ID);
            perforRescomputeRepository.AddRange(computes.ToArray());

            baiscnormList.ForEach(t => t.AllotID = allot.ID);
            logManageService.WriteMsg("正在生成绩效", "保存最终绩效数据", 1, allot.ID, "ReceiveMessage");

            return baiscnormList;
        }


        /// <summary>
        /// 特殊科室绩效计算
        /// </summary>
        /// <param name="excel"></param>
        /// <param name="allot"></param>
        public void SpecialUnitCompute(PerExcel excel, per_allot allot, List<PerSheet> sheetLast, List<res_baiscnorm> baiscnormList,
            IEnumerable<AccountUnitTotal> accountExtras, IEnumerable<AccountUnitTotal> drugExtras, IEnumerable<AccountUnitTotal> materialsExtras,
            IEnumerable<EmpolyeeTotal> employeeExtra)
        {
            var isBudget = budgetService.GetAdjustAndGrant(allot, out decimal adjust, out decimal grant);
            var typeList = EnumHelper.GetItems<PerforType>();

            var specialUnit = excel.PerSheet.FirstOrDefault(t => t.SheetType == SheetType.SpecialUnit);
            if (specialUnit == null || specialUnit.PerData.Count == 0)
                return;

            var dataList = xxx(specialUnit, allot, baiscnormList, typeList);

            //取出科室
            var accountList = excel.PerSheet.FirstOrDefault(t => t.SheetType == SheetType.AccountBasic)?.PerData?.Select(t => (PerDataAccountBaisc)t);

            List<res_specialunit> resDataList = new List<res_specialunit>();

            var groupSpeList = dataList.GroupBy(t => t.AccountingUnit).Select(t => new { AccountingUnit = t.Key, Number = t.Max(p => p.Number), });

            var empolyeeList = perforImemployeeclinicRepository.GetEntities(t => t.AllotID == allot.ID && t.UnitType == UnitType.特殊核算组.ToString());
            List<ComputeEmployee> computeEmployees = Mapper.Map<List<ComputeEmployee>>(empolyeeList);
            List<ComputeResult> computeList = new List<ComputeResult>();

            foreach (var group in groupSpeList)
            {
                // 获取科室测算信息
                var dept = accountList?.FirstOrDefault(t => t.UnitType == UnitType.特殊核算组.ToString() && t.AccountingUnit == group.AccountingUnit);
                //获取需要聚合的科室
                var accountDataList = dataList.Where(t => t.AccountingUnit == group.AccountingUnit);
                //计算量化指标子项合计
                var sumValue = accountDataList.Sum(t =>
                {
                    // 新都医院在特殊科室计算时，如果取核算基数计算时带入人数计算
                    decimal? headcount = null;
                    if (typeList.Any(o => o.Description == t.QuantitativeIndicators))
                        headcount = group.Number;
                    if (!headcount.HasValue || headcount == 0)
                        headcount = 1;
                    return t.Quantity * t.QuantitativeIndicatorsValue * headcount;
                });


                var groupAdjust = isBudget ? adjust : (dept?.AdjustFactor ?? 0);
                foreach (var item in accountDataList)
                {
                    //科室奖罚汇总结果
                    var extra = accountExtras?.FirstOrDefault(w => w.UnitType == UnitType.特殊核算组.ToString() && w.AccountingUnit == dept.AccountingUnit)?.TotelValue;
                    var drugExtra = drugExtras?.FirstOrDefault(w => w.UnitType == UnitType.特殊核算组.ToString() && w.AccountingUnit == dept.AccountingUnit)?.TotelValue;
                    var materialsExtra = materialsExtras?.FirstOrDefault(w => w.UnitType == UnitType.特殊核算组.ToString() && w.AccountingUnit == dept.AccountingUnit)?.TotelValue;

                    var res = new res_specialunit
                    {
                        AllotID = allot.ID,
                        AccountingUnit = group.AccountingUnit,
                        Department = group.AccountingUnit,
                        Number = group.Number,
                        QuantitativeIndicators = item.QuantitativeIndicators,
                        Quantity = item.Quantity,
                        QuantitativeIndicatorsValue = item.QuantitativeIndicatorsValue,
                        ScoringAverage = dept?.ScoringAverage,
                        //OtherPerfor = dept?.OtherPerfor,
                        Punishment = (extra ?? 0),
                        MedicineExtra = (drugExtra ?? 0),
                        MaterialsExtra = (materialsExtra ?? 0),
                        Adjust = (groupAdjust == 0) ? 1 : groupAdjust,
                    };

                    res.GiveFee = Math.Round((sumValue + (extra ?? 0)) ?? 0);
                    res.RealGiveFee = Math.Round(res.GiveFee * (res.ScoringAverage ?? 0) * (groupAdjust == 0 ? 1 : groupAdjust) ?? 0);
                    res.Avg = Math.Round((group.Number != 0 ? res.GiveFee / group.Number : null) ?? 0);
                    res.ResultsTotalFee = Math.Round(sumValue ?? 0);

                    resDataList.Add(res);
                }

                if (computeEmployees == null || !computeEmployees.Any()) continue;

                var empolyees = computeEmployees.Where(t => t.AccountingUnit == group.AccountingUnit);
                if (empolyees == null || !empolyees.Any()) continue;

                foreach (var empolyee in empolyees)
                {
                    var extra = employeeExtra?.Where(w => !string.IsNullOrEmpty(w.AccountingUnit) && !string.IsNullOrEmpty(w.UnitType)
                            && (!string.IsNullOrEmpty(w.EmployeeName) || !string.IsNullOrEmpty(w.JobNumber)))
                        .FirstOrDefault(w => w.AccountingUnit == empolyee.AccountingUnit && w.UnitType == empolyee.UnitType
                            && w.JobNumber == empolyee.JobNumber && w.EmployeeName == empolyee.DoctorName)?.TotelValue;

                    var empolyeeAdjust = isBudget ? adjust : empolyee.Adjust;
                    var compute = new ComputeResult
                    {
                        UnitType = UnitType.特殊核算组.ToString(),
                        AccountingUnit = group.AccountingUnit,

                        AccountType = AccountUnitType.科主任.ToString(),
                        EmployeeName = empolyee.DoctorName,
                        FitPeople = empolyee.FitPeople,
                        JobTitle = empolyee.JobTitle,

                        ScoreAverageRate = empolyee.ScoreAverageRate,
                        //Punishment = empolyee.Punishment,
                        //OtherPerfor = empolyee.OtherPerfor,

                        Number = group.Number,
                        PerforTotal = sumValue,
                        Avg = group.Number == 0 ? 0 : (sumValue / group.Number),
                        Adjust = empolyeeAdjust,
                        Grant = isBudget ? grant : empolyee.Management
                    };

                    compute.Efficiency = compute.Avg * (empolyee.Efficiency ?? 1);
                    compute.Scale = compute.PerforTotal * (empolyee.Scale ?? 1);
                    //应发管理绩效
                    compute.ShouldGiveFee = Math.Round((compute.Efficiency + compute.Scale) * compute.Grant ?? 0);
                    //绩效合计
                    compute.PerforSumFee = Math.Round(compute.Avg + compute.ShouldGiveFee ?? 0);
                    //应发绩效
                    //compute.GiveFee = compute.PerforSumFee;
                    compute.GiveFee = Math.Round(compute.PerforSumFee * compute.ScoreAverageRate * (compute.Attendance ?? 0) + (extra ?? 0) ?? 0);
                    //实发绩效
                    //compute.RealGiveFee = (compute.GiveFee * compute.ScoreAverageRate + (compute.Punishment ?? 0) + (compute.OtherPerfor ?? 0)) * (compute.Adjust ?? 1m);
                    compute.RealGiveFee = Math.Round(compute.GiveFee * (compute.Adjust ?? 1m) ?? 0);
                    // 参考基数专用绩效合计
                    compute.BaiscNormPerforTotal = compute.PerforSumFee;

                    computeList.Add(compute);
                }
            }
            var computes = Mapper.Map<List<res_compute>>(computeList);
            computes.ForEach(t => t.AllotID = allot.ID);
            perforRescomputeRepository.AddRange(computes.ToArray());
            perforResspecialunitRepository.AddRange(resDataList.ToArray());
        }

        private IEnumerable<PerDataSpecialUnit> xxx(PerSheet specialUnit, per_allot allot, List<res_baiscnorm> baiscnormList, List<EnumItem> typeList)
        {
            var dataList = specialUnit.PerData.Select(t => (PerDataSpecialUnit)t);
            var specialEmployee = perforImemployeeclinicRepository.GetEntities(t => t.AllotID == allot.ID);

            //替换考核基数
            foreach (var item in dataList)
            {
                if (specialEmployee != null && specialEmployee.FirstOrDefault(t => t.AccountingUnit == item.AccountingUnit) != null)
                {
                    item.Efficiency = specialEmployee.FirstOrDefault(t => t.AccountingUnit == item.AccountingUnit).Efficiency ?? 1;
                    item.Scale = specialEmployee.FirstOrDefault(t => t.AccountingUnit == item.AccountingUnit).Scale ?? 1;
                    item.Management = specialEmployee.FirstOrDefault(t => t.AccountingUnit == item.AccountingUnit).Management ?? 1;
                }
                var type = typeList.FirstOrDefault(o => o.Description == item.QuantitativeIndicators);
                if (type != null)
                {
                    if (type.Value == (int)PerforType.医生护士平均)
                    {
                        var doctor = baiscNormService.GetBaiscNorm(baiscnormList, PerforType.临床医生) ?? 0;
                        var nurse = baiscNormService.GetBaiscNorm(baiscnormList, PerforType.护士) ?? 0;
                        // 添加参数计算
                        item.Quantity = item.Quantity ?? (doctor + nurse) / 2;
                    }
                    else
                    {
                        //var radio = perforImEmployeeRepository.GetEntities(p => p.FitPeople == EnumHelper.GetDescription((PerforType)type.Value) && p.AllotID == allot.ID)
                        //    ?.FirstOrDefault().FitPeopleRatio ?? 1;
                        var basic = baiscNormService.GetBaiscNorm(baiscnormList, (PerforType)type.Value);
                        // 添加参数计算
                        item.Quantity = item.Quantity ?? (basic ?? 0);
                    }
                }
            }
            return dataList;
        }
    }
}
