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

namespace Performance.Services
{
    /// <summary>
    /// 临床科室主任
    /// </summary>
    public class ComputeDirector : IAutoInjection
    {
        private readonly BaiscNormService baiscNormService;
        private readonly PerforCofdirectorRepository perforCofdirectorRepository;
        private readonly PerforCofworkyearRepository perforCofworkyearRepository;
        private readonly PerforResaccountRepository perforResaccountRepository;
        //private readonly PerforResaccountdoctorRepository perforResAccountdoctorRepository;

        public ComputeDirector(BaiscNormService baiscNormService,
            PerforCofdirectorRepository perforCofdirectorRepository,
            PerforCofworkyearRepository perforCofworkyearRepository,
            PerforResaccountRepository perforResaccountRepository)
        {
            this.baiscNormService = baiscNormService;
            this.perforCofdirectorRepository = perforCofdirectorRepository;
            this.perforCofworkyearRepository = perforCofworkyearRepository;
            this.perforResaccountRepository = perforResaccountRepository;
        }

        #region 老版计算
        ///// <summary>
        ///// 临床科室主任、临床科室副主任、临床科室护士长 计算
        ///// </summary>
        ///// <param name="empolyeeList"></param>
        ///// <param name="nurseList"></param>
        ///// <param name="directorList"></param>
        ///// <returns></returns>
        //public List<ComputeResult> Compute(List<ComputeEmployee> empolyeeList, List<im_accountbasic> accountbasicList, per_allot allot)
        //{
        //    //规模绩效和效率绩效配置表
        //    var directorList = perforCofdirectorRepository.GetEntities(t => t.AllotID == allot.ID);
        //    //取出医生科室
        //    var doctorList = perforResAccountdoctorRepository.GetEntities(t => t.AllotID == allot.ID);
        //    //取出护士科室
        //    var nurseList = perforResAccountnurseRepository.GetEntities(t => t.AllotID == allot.ID);

        //    List<ComputeResult> computeList = new List<ComputeResult>();
        //    // 主任及护士长 对应 效率绩效/规模绩效 关系
        //    // 科室名称 确定绩效计算人员中的人群
        //    // 绩效考核基数参考对象  确定科室下那些考核对象归纳为 科室主任  护士长
        //    // 数据库存放的文本 与枚举 全文字中文匹配
        //    var basicRuleList = new[]
        //    {
        //        new { AccountUnitType = AccountUnitType.临床科室, PerforType = PerforType.临床主任, DirectorType = DirectorType.临床科室主任 },
        //        new { AccountUnitType = AccountUnitType.临床科室, PerforType = PerforType.临床副主任, DirectorType = DirectorType.临床科室副主任 },
        //        new { AccountUnitType = AccountUnitType.医技科室, PerforType = PerforType.医技主任, DirectorType = DirectorType.医技科室主任 },
        //        new { AccountUnitType = AccountUnitType.医技科室, PerforType = PerforType.医技副主任, DirectorType = DirectorType.医技科室副主任 },
        //        new { AccountUnitType = AccountUnitType.临床科室, PerforType = PerforType.护士长, DirectorType = DirectorType.临床科室护士长 },
        //    };
        //    foreach (var basicRule in basicRuleList)
        //    {
        //        var needCompute = empolyeeList.Where(t => t.AccountType == basicRule.AccountUnitType.ToString() && t.FitPeople.Trim() == EnumHelper.GetDescription(basicRule.PerforType));
        //        foreach (var item in needCompute)
        //        {
        //            var accountbasic = accountbasicList.FirstOrDefault(t => t.Department == item.Department);
        //            if (accountbasic == null) continue;

        //            //分别取出对应的 计算 人员 平均值等信息
        //            decimal? number, perforTotal, avg;
        //            if (basicRule.PerforType == PerforType.护士长)
        //            {
        //                if (!accountbasic.NurseHeadNumber.HasValue || accountbasic.NurseHeadNumber.Value == 0) continue;

        //                var resAccount = nurseList.FirstOrDefault(t => t.AccountingUnit == item.AccountingUnit);
        //                number = resAccount?.Number;
        //                perforTotal = resAccount?.PerforTotal;
        //                avg = resAccount?.Avg;
        //            }
        //            else
        //            {
        //                if (!accountbasic.DoctorDirectorNumber.HasValue || accountbasic.DoctorDirectorNumber.Value == 0) continue;

        //                var resAccount = doctorList.FirstOrDefault(t => t.AccountingUnit == item.AccountingUnit);
        //                number = resAccount?.Number;
        //                perforTotal = resAccount?.PerforTotal;
        //                avg = resAccount?.Avg;
        //            }
        //            var efficiency = (basicRule.PerforType == PerforType.护士长) ? accountbasic.NurseEffic : accountbasic.DoctorEffic;
        //            var scale = (basicRule.PerforType == PerforType.护士长) ? accountbasic.NurseScale : accountbasic.DoctorScale;
        //            var grant = (basicRule.PerforType == PerforType.护士长) ? accountbasic.NurseGrant : accountbasic.DoctorGrant;

        //            var compute = new ComputeResult
        //            {
        //                AccountType = item.AccountType,
        //                AccountingUnit = item.AccountingUnit,
        //                EmployeeName = item.DoctorName,
        //                FitPeople = item.FitPeople,
        //                JobTitle = item.JobTitle,

        //                Number = number,
        //                PerforTotal = perforTotal,
        //                Avg = avg,
        //                Efficiency = avg * (efficiency ?? 1),
        //                Scale = perforTotal * (scale ?? 1),
        //                //Grant = item.Grant ?? 1,
        //                ScoreAverageRate = item.ScoreAverageRate,
        //                Punishment = item.Punishment,
        //                OtherPerfor = item.OtherPerfor,
        //                Adjust = item.Adjust,
        //                //Workload = item.Workload
        //            };
        //            //应发管理绩效
        //            compute.ShouldGiveFee = (compute.Efficiency + compute.Scale) * grant;
        //            //绩效合计
        //            compute.PerforSumFee = compute.Avg + compute.ShouldGiveFee;
        //            //应发绩效
        //            compute.GiveFee = compute.PerforSumFee * compute.ScoreAverageRate + (item.Punishment ?? 0) + (item.OtherPerfor ?? 0);
        //            //实发绩效
        //            compute.RealGiveFee = compute.GiveFee * (item.Adjust ?? 1m);
        //            computeList.Add(compute);
        //        }

        //    }
        //    return computeList;
        //} 
        #endregion

        #region 北京东方模板改版注释20200228
        ///// <summary>
        ///// 临床科室主任、临床科室副主任、临床科室护士长 计算
        ///// </summary>
        ///// <param name="empolyeeList"></param>
        ///// <param name="nurseList"></param>
        ///// <param name="directorList"></param>
        ///// <returns></returns>
        //public List<ComputeResult> Compute2(List<ComputeEmployee> empolyeeList, List<im_accountbasic> accountbasicList, List<PerSheet> accountSheet, per_allot allot, bool isMinimum)
        //{
        //    var basicRuleList = new[]
        //    {
        //        new { AccountUnitType = AccountUnitType.科主任, UnitType = UnitType.医生组 },
        //        new { AccountUnitType = AccountUnitType.护士长, UnitType = UnitType.护理组 },
        //        new { AccountUnitType = AccountUnitType.科主任, UnitType = UnitType.医技组 },
        //    };
        //    //取出科室
        //    List<res_account> dataList = new List<res_account>();
        //    foreach (var account in accountSheet)
        //    {
        //        dataList.AddRange(AutoMapper.Mapper.Map<List<res_account>>(account.PerData.Select(t => (PerDataAccountBaisc)t)));
        //    }
        //    var multi = accountbasicList.GroupBy(t => new { t.UnitType, t.DoctorAccountingUnit })
        //                    .Select(t => new { t.Key.UnitType, t.Key.DoctorAccountingUnit, Count = t.Count(), })
        //                    .Where(t => t.Count > 1);
        //    List<ComputeResult> computeList = new List<ComputeResult>();
        //    foreach (var accountbasic in accountbasicList)
        //    {
        //        if (!accountbasic.DoctorDirectorNumber.HasValue || accountbasic.DoctorDirectorNumber.Value == 0)
        //            continue;
        //        //if ((!accountbasic.DoctorDirectorNumber.HasValue && "皮肤科" != accountbasic.DoctorAccountingUnit) || (accountbasic.DoctorDirectorNumber.Value == 0 && "皮肤科" != accountbasic.DoctorAccountingUnit))
        //        //    continue;

        //        //原不存在科主任则跳过科主任绩效计算20190920新都
        //        //是否共用核算单元
        //        var isShare = multi.Any(group => group.UnitType == accountbasic.UnitType && group.DoctorAccountingUnit == accountbasic.DoctorAccountingUnit);
        //        if (isShare
        //            && accountbasic.DoctorDirectorNumber.HasValue && accountbasic.DoctorDirectorNumber.Value < 1
        //            && accountbasic.Department != accountbasic.DoctorAccountingUnit)
        //            continue;
        //        //分别取出对应的 计算 人员 平均值等信息 
        //        var resAccount = dataList.Where(t => t.UnitType == accountbasic.UnitType && t.AccountingUnit == accountbasic.DoctorAccountingUnit);
        //        var number = resAccount.OrderByDescending(t => t.ManagerNumber).Sum(t => t.ManagerNumber + t.Number);
        //        var perforTotal = resAccount.Sum(t => t.PerforTotal);
        //        //var avg = resAccount.Average(t => t.Avg);
        //        //绩效参考标准，不使用实发绩效             
        //        //var realAvg = resAccount.Sum(t => (t.ManagerNumber + t.Number) == 0 ? 0 : t.RealGiveFee / (t.ManagerNumber + t.Number)) / resAccount.Count();
        //        //var realAvg = resAccount.Sum(t => t.Number + t.ManagerNumber) == 0 ? 0 : resAccount.Sum(t => t.RealGiveFee) / resAccount.Sum(t => t.Number + t.ManagerNumber);

        //        decimal? realAvg = 0, realAvg2 = 0;
        //        bool isAvg = true;
        //        //if ("内五科" == accountbasic.DoctorAccountingUnit && isShare)
        //        //    realAvg = resAccount.Sum(r => r.Number + r.ManagerNumber) == 0 ? 0 : resAccount.Sum(r => r.RealGiveFee) / resAccount.Sum(r => r.Number);
        //        //else
        //        //{
        //        foreach (var item in resAccount)
        //        {
        //            if ((item.ManagerNumber + item.Number) == 0)
        //            {
        //                isAvg = false;
        //                realAvg = resAccount.Sum(r => r.Number + r.ManagerNumber) == 0 ? 0 : resAccount.Sum(r => r.RealGiveFee) / resAccount.Sum(r => r.Number + r.ManagerNumber);
        //                realAvg2 = resAccount.Sum(r => r.PermanentStaff) == 0 ? 0 : resAccount.Sum(r => r.RealGiveFee) / resAccount.Sum(r => r.PermanentStaff);
        //                break;
        //            }
        //            else
        //            {
        //                realAvg += item.RealGiveFee / (item.ManagerNumber + item.Number);
        //                realAvg2 += item.PermanentStaff == 0 ? 0 : item.RealGiveFee / item.PermanentStaff;
        //            }
        //        }
        //        if (isAvg)
        //        {
        //            realAvg = realAvg / resAccount.Count();
        //            realAvg2 = realAvg2 / resAccount.Count();
        //        }
        //        //}

        //        var realGiveFee = resAccount.Sum(t => t.RealGiveFee);


        //        var basicRule = basicRuleList.FirstOrDefault(t => t.UnitType == (UnitType)accountbasic.UnitType);
        //        if (basicRule == null) continue;
        //        var empolyees = empolyeeList.Where(t => t.UnitType == basicRule.UnitType.ToString() && t.AccountingUnit == accountbasic.DoctorAccountingUnit);
        //        foreach (var empolyee in empolyees)
        //        {
        //            //if (empolyee == null)
        //            //{
        //            //    empolyee = new ComputeEmployee
        //            //    {
        //            //        AccountType = basicRule.AccountUnitType.ToString(),
        //            //        DoctorName = "人员信息缺失",
        //            //        FitPeople = "",
        //            //        ScoreAverageRate = 1,
        //            //        Punishment = 0,
        //            //        OtherPerfor = 0,
        //            //        Adjust = 1,
        //            //    };
        //            //}
        //            var compute = new ComputeResult
        //            {
        //                UnitType = basicRule.UnitType.ToString(),
        //                AccountingUnit = accountbasic.DoctorAccountingUnit,

        //                AccountType = basicRule.AccountUnitType.ToString(),
        //                EmployeeName = empolyee.DoctorName,
        //                FitPeople = empolyee.FitPeople,
        //                JobTitle = empolyee.JobTitle,
        //                JobNumber = empolyee.JobNumber,

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

        //                Number = number,
        //                PerforTotal = realGiveFee,
        //                Avg = realAvg,
        //                Efficiency = realAvg2 * (empolyee.Efficiency ?? 1),
        //                Scale = perforTotal * (empolyee.Scale ?? 1),
        //                Adjust = empolyee.Adjust,
        //                Remark = isShare ? "特殊科室主任，共用核算单元" : ""
        //            };
        //            //应发管理绩效
        //            compute.ShouldGiveFee = (compute.Efficiency + compute.Scale) * (empolyee.Management ?? 0);
        //            //绩效合计
        //            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);
        //        }
        //    }
        //    return computeList;
        //}
        #endregion

        /// <summary>
        /// 临床科室主任、临床科室副主任、临床科室护士长 计算
        /// </summary>
        /// <param name="empolyeeList"></param>
        /// <param name="nurseList"></param>
        /// <param name="directorList"></param>
        /// <returns></returns>
        public List<ComputeResult> Compute(List<ComputeEmployee> empolyeeList, List<im_accountbasic> accountbasicList, List<PerSheet> accountSheet, per_allot allot, bool isMinimum)
        {
            var basicRuleList = new[]
              {
                new { AccountUnitType = AccountUnitType.科主任, UnitType = UnitType.医生组 },
                new { AccountUnitType = AccountUnitType.护士长, UnitType = UnitType.护理组 },
                new { AccountUnitType = AccountUnitType.科主任, UnitType = UnitType.医技组 },
            };
            //取出科室
            List<res_account> dataList = new List<res_account>();
            foreach (var account in accountSheet)
            {
                dataList.AddRange(AutoMapper.Mapper.Map<List<res_account>>(account.PerData.Select(t => (PerDataAccountBaisc)t)));
            }
            List<ComputeResult> computeList = new List<ComputeResult>();

            var empDis = empolyeeList.Select(w => new { w.DoctorName, w.JobNumber }).Distinct();
            foreach (var emp in empDis)
            {
                var empolyees = empolyeeList.Where(w => w.DoctorName == emp.DoctorName && w.JobNumber == emp.JobNumber);
                for (int i = 0; i < empolyees.Count(); i++)
                {
                    var empolyee = empolyees.ElementAt(i);

                    var resAccount = dataList.FirstOrDefault(t => ((UnitType)t.UnitType).ToString() == empolyee.UnitType && t.AccountingUnit == empolyee.AccountingUnit);
                    if (resAccount == null)
                    {
                        // 如果没有找到科室，则找相同用户的其他核算单元类型
                        for (int j = 0; j < empolyees.Count(); j++)
                        {
                            if (i == j) continue;
                            var empolyee2 = empolyees.ElementAt(j);
                            resAccount = dataList.FirstOrDefault(t => ((UnitType)t.UnitType).ToString() == empolyee2.UnitType && t.AccountingUnit == empolyee2.AccountingUnit);
                            if (resAccount != null) continue;
                        }
                        if (resAccount == null) continue;
                    }

                    var basicRule = basicRuleList.FirstOrDefault(t => t.UnitType == (UnitType)resAccount.UnitType);
                    if (basicRule == null) continue;

                    var avg = (resAccount.ManagerNumber + resAccount.Number) == 0 ? 0 : resAccount.RealGiveFee / (resAccount.ManagerNumber + resAccount.Number);
                    var effAvg = resAccount.PermanentStaff == 0 ? 0 : resAccount.RealGiveFee / resAccount.PermanentStaff;

                    var efficiency = avg * (empolyee.Efficiency ?? 1);
                    var scale = resAccount.PerforTotal * (empolyee.Scale ?? 1);

                    var compute = new ComputeResult
                    {
                        UnitType = basicRule.UnitType.ToString(),
                        AccountingUnit = empolyee.AccountingUnit,

                        AccountType = basicRule.AccountUnitType.ToString(),
                        EmployeeName = empolyee.DoctorName,
                        FitPeople = empolyee.FitPeople,
                        JobTitle = empolyee.JobTitle,
                        JobNumber = empolyee.JobNumber,

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

                        Number = resAccount.ManagerNumber + resAccount.Number,
                        PerforTotal = resAccount.RealGiveFee,
                        Avg = avg,
                        Efficiency = effAvg * (empolyee.Efficiency ?? 1),
                        Scale = resAccount.PerforTotal * (empolyee.Scale ?? 1),
                        Adjust = empolyee.Adjust,
                        Remark = empolyees.Count() > 1 ? "特殊科室主任，共用核算单元" : ""
                    };
                    //应发管理绩效
                    compute.ShouldGiveFee = (compute.Efficiency + compute.Scale) * (empolyee.Management ?? 0);
                    //绩效合计
                    compute.PerforSumFee = (compute.Avg * (empolyee.Basics ?? 0)) + 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);
                }
            }
            return computeList;
        }

        /// <summary>
        /// 院领导、业务中层、工勤人员 计算
        /// </summary>
        /// <param name="empolyeeList"></param>
        /// <param name="baiscnormList"></param>
        /// <returns></returns>
        public List<ComputeResult> Compute(List<ComputeEmployee> empolyeeList, per_allot allot, List<res_baiscnorm> baiscnormList)
        {
            //年资系数
            var workyearList = perforCofworkyearRepository.GetEntities(t => t.AllotID == allot.ID);

            List<ComputeResult> computeList = new List<ComputeResult>();

            List<string> involve = new List<string> { AccountUnitType.行政高层.ToString(), AccountUnitType.行政中层.ToString(), AccountUnitType.行政工勤.ToString() };
            var needCompute = empolyeeList.Where(t => involve.Contains(t.AccountType));

            if (!needCompute.Any()) return computeList;

            var perforTypeArray = EnumHelper.GetItems<PerforType>();
            foreach (var item in needCompute)
            {
                var compute = new ComputeResult
                {
                    AccountType = item.AccountType,
                    AccountingUnit = item.AccountingUnit,
                    EmployeeName = item.DoctorName,
                    FitPeople = item.FitPeople,
                    //Grant = item.Grant ?? 1,
                    WorkTime = item.WorkTime,
                    PostCoefficient = item.PostCoefficient,
                    Attendance = item.Attendance,
                    ScoreAverageRate = item.ScoreAverageRate,
                    Punishment = item.Punishment,
                    OtherPerfor = item.OtherPerfor,
                    JobTitle = item.JobTitle,
                    JobNumber = item.JobNumber,
                    Adjust = item.Adjust,
                    //Workload = item.Workload

                };
                decimal? baiscnorm = 1;
                var perforTypeItem = perforTypeArray.FirstOrDefault(t => t.Description == item.FitPeople);
                if (perforTypeItem != null)
                {
                    var perforType = (PerforType)perforTypeItem.Value;
                    baiscnorm = baiscNormService.GetBaiscNorm(baiscnormList, perforType);

                    //年资系数
                    if (item.FitPeople == AccountUnitType.行政工勤.ToString() && item.WorkTime.HasValue && item.WorkTime.Value > new DateTime(1970, 1, 1))
                    {
                        var years = ((DateTime.Now.Year - item.WorkTime.Value.Year) * 12 + (DateTime.Now.Month - item.WorkTime.Value.Month)) / 12.0m;
                        var value = workyearList.FirstOrDefault(t => t.MinRange < years && years <= t.MaxRange)?.Value;
                        compute.WorkYear = value;
                    }
                }
                //添加参数计算
                compute.BaiscNormValue = baiscnorm * (item.FitPeopleRatio == null || item.FitPeopleRatio == 0 ? 1 : item.FitPeopleRatio);
                //应发绩效
                compute.GiveFee = compute.BaiscNormValue * compute.PostCoefficient * (compute.WorkYear ?? 1) * compute.Attendance * compute.ScoreAverageRate
                   + (compute.OtherPerfor ?? 0) + (compute.Punishment ?? 0);
                //实发绩效
                compute.RealGiveFee = compute.GiveFee * (item.Adjust ?? 1);
                computeList.Add(compute);
            }

            return computeList;
        }
    }
}
