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 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;

        public ResultComputeService(
            PerforImemployeeRepository perforImEmployeeRepository,
            PerforRescomputeRepository perforRescomputeRepository,
            PerforResbaiscnormRepository perforResbaiscnormRepository,
            PerforResspecialunitRepository perforResspecialunitRepository,
            PerforImaccountbasicRepository perforImaccountbasicRepository,
            BaiscNormService baiscNormService, ComputeDirector computeDirector,
            LogManageService logManageService,
            PerforImemployeeclinicRepository perforImemployeeclinicRepository)
        {
            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.perforImemployeeclinicRepository = perforImemployeeclinicRepository;
        }

        /// <summary>
        /// 计算最终数据
        /// </summary>
        /// <param name="excel"></param>
        public List<res_baiscnorm> Compute(per_allot allot, PerExcel excel, List<PerSheet> accountSheet, bool isMinimum = false)
        {
            //取出人员信息           
            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, accountbasicList, accountSheet, allot, isMinimum);
            //计算 绩效标准 基数（科主任、副主任、护士长 =>> 平均值）
            List<res_baiscnorm> baiscnormList = new List<res_baiscnorm>();
            baiscNormService.ComputeAvg(baiscnormList, accountbasicList, computResult);
            baiscNormService.DocterNurseBaiscnorm(baiscnormList, accountbasicList, accountSheet);

            var empolyeeList1 = perforImEmployeeRepository.GetEntities(t => t.AllotID == allot.ID);
            computeEmployees = Mapper.Map<List<ComputeEmployee>>(empolyeeList1);
            logManageService.WriteMsg("正在生成绩效", "院领导、业务中层、工勤人员 最终绩效数据计算", 1, allot.ID, "ReceiveMessage");
            var computResult2 = computeDirector.Compute(computeEmployees, allot, baiscnormList);
            //计算 行政人员 平均值
            baiscNormService.ComputeOtherAvg(baiscnormList, computResult2, empolyeeList1);

            if (!isMinimum)
            {
                var computes = Mapper.Map<List<res_compute>>(computResult);
                computes.AddRange(Mapper.Map<List<res_compute>>(computResult2));
                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<res_baiscnorm> baiscnormList)
        {
            var specialUnit = excel.PerSheet.FirstOrDefault(t => t.SheetType == SheetType.SpecialUnit);
            if (specialUnit == null || specialUnit.PerData.Count == 0)
                return;
            var typeList = EnumHelper.GetItems<PerforType>();
            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 = (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 = basic != null ? basic * radio : null;
                    }
                }
            }

            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),
                ScoringAverage = t.Max(p => p.ScoringAverage),
                OtherPerfor = t.Max(p => p.OtherPerfor),
                Punishment = t.Max(p => p.Punishment),
                Adjust = t.Max(p => p.Adjust),
            });

            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 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;
                });
                foreach (var item in accountDataList)
                {
                    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 = group.ScoringAverage,
                        OtherPerfor = group.OtherPerfor,
                        Punishment = group.Punishment,
                        Adjust = group.Adjust == 0 ? 1 : group.Adjust,
                    };
                    res.GiveFee = (sumValue + (group.OtherPerfor ?? 0) + (group.Punishment ?? 0));
                    res.RealGiveFee = res.GiveFee * (group.Adjust == 0 ? 1 : group.Adjust);
                    res.Avg = group.Number != 0 ? res.GiveFee / group.Number : null;
                    res.ResultsTotalFee = sumValue;
                    resDataList.Add(res);
                }


                var empolyees = computeEmployees.Where(t => t.AccountingUnit == group.AccountingUnit);
                foreach (var empolyee in empolyees)
                {
                    //if (empolyee == null)
                    //{
                    //    empolyee = new ComputeEmployee
                    //    {
                    //        AccountType = AccountUnitType.科主任.ToString(),
                    //        DoctorName = "人员信息缺失",
                    //        FitPeople = "",
                    //        ScoreAverageRate = 1,
                    //        Punishment = 0,
                    //        OtherPerfor = 0,
                    //        Adjust = 1,
                    //    };
                    //}
                    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 = empolyee.Adjust
                    };

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

                    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());
        }
    }
}
