using AutoMapper;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using NPOI.XSSF.UserModel;
using OfficeOpenXml.FormulaParsing.Excel.Functions.DateTime;
using Performance.DtoModels;
using Performance.DtoModels.AppSettings;
using Performance.EntityModels;
using Performance.Infrastructure;
using Performance.Repository;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Performance.Services
{
    public class SecondAllotDetails : IAutoInjection
    {
        private readonly IMapper _mapper;
        private readonly ILogger<SecondAllotDetails> _logger;
        private readonly PerforImemployeelogisticsRepository _imemployeelogisticsRepository;
        private readonly Application _application;
        private readonly PerforAgsecondallotRepository agsecondallotRepository;
        private readonly PerforAgusetempRepository agusetempRepository;
        private readonly PerforAgtempitemRepository agtempitemRepository;
        private readonly PerforAgworkloadRepository agworkloadRepository;
        private readonly PerforAgworkloadtypeRepository agworkloadtypeRepository;
        private readonly PerforAgfixatitemRepository agfixatitemRepository;
        private readonly PerforAgothersourceRepository agothersourceRepository;
        private readonly PerforPerallotRepository perallotRepository;
        private readonly PerforCofagainRepository cofagainRepository;
        private readonly PerforPerapramountRepository perapramountRepository;
        private readonly PerforRescomputeRepository rescomputeRepository;
        private readonly PersonService personService;
        private readonly UserService _userService;

        public SecondAllotDetails(
            IMapper mapper,
            ILogger<SecondAllotDetails> logger,
            IOptions<Application> application,
            PerforImemployeelogisticsRepository imemployeelogisticsRepository,
            PerforAgsecondallotRepository agsecondallotRepository,
            PerforAgusetempRepository agusetempRepository,
            PerforAgtempitemRepository agtempitemRepository,
            PerforAgworkloadRepository agworkloadRepository,
            PerforAgworkloadtypeRepository agworkloadtypeRepository,
            PerforAgfixatitemRepository agfixatitemRepository,
            PerforAgothersourceRepository agothersourceRepository,
            PerforPerallotRepository perallotRepository,
            PerforCofagainRepository cofagainRepository,
            PerforPerapramountRepository perapramountRepository,
            PerforRescomputeRepository rescomputeRepository,
            PersonService personService,
            UserService userService
            )
        {
            _mapper = mapper;
            _logger = logger;
            _imemployeelogisticsRepository = imemployeelogisticsRepository;
            _application = application.Value;
            this.agsecondallotRepository = agsecondallotRepository;
            this.agusetempRepository = agusetempRepository;
            this.agtempitemRepository = agtempitemRepository;
            this.agworkloadRepository = agworkloadRepository;
            this.agworkloadtypeRepository = agworkloadtypeRepository;
            this.agfixatitemRepository = agfixatitemRepository;
            this.agothersourceRepository = agothersourceRepository;
            this.perallotRepository = perallotRepository;
            this.cofagainRepository = cofagainRepository;
            this.perapramountRepository = perapramountRepository;
            this.rescomputeRepository = rescomputeRepository;
            this.personService = personService;
            _userService = userService;
        }

        #region 横向纵向模板详情

        /// <summary>
        /// 二次绩效详情
        /// </summary>
        /// <param name="userId"></param>
        /// <param name="secondId"></param>
        /// <param name="hospitalId"></param>
        /// <param name="isArchive"></param>
        /// <returns></returns>
        public SecondResponse GetSecondDetails(int userId, int secondId, int hospitalId, int isArchive, int employeeSource)
        {
            var secondAllot = agsecondallotRepository.GetEntity(w => w.Id == secondId);
            if (secondAllot == null) throw new PerformanceException("未查询到二次绩效信息");

            var prevSecondAllot = GetPreviousSecondAllot(hospitalId, secondAllot);

            int tempId = (int)Temp.other;
            var temp = agusetempRepository.GetEntities(w => w.HospitalId == hospitalId);
            var userTemp = temp.FirstOrDefault(w => UnitTypeUtil.Is(w.UnitType, secondAllot.UnitType) && w.Department == secondAllot.Department);
            if (userTemp == null)
                userTemp = temp?.FirstOrDefault(w => UnitTypeUtil.Is(w.UnitType, secondAllot.NewUnitType) && w.Department == secondAllot.NewAccountingUnit);

            if (userTemp != null) tempId = userTemp.UseTempId ?? (int)Temp.other;
            if (new int[] { 2, 3 }.Contains(secondAllot.Status ?? 1))   //纪录被提交后，根据提交时的模板获取对应的数据
                tempId = (secondAllot.UseTempId ?? 0) == 0 ? (int)Temp.other : secondAllot.UseTempId.Value;

            if (tempId == (int)Temp.other) return new SecondResponse();

            // 历史保存过的数据，groupby取最后的一条记录，避免重复数据，在同一rownumber中itemname重复会导致数据丢失
            var savedDataList = agfixatitemRepository.GetEntities(w => w.SecondId == secondAllot.Id);
            if (savedDataList != null && savedDataList.Any())
                savedDataList = savedDataList.GroupBy(t => new { t.RowNumber, t.ItemName, t.Sort })
                    .Select(t => t.OrderByDescending(o => o.ID).FirstOrDefault()).ToList();

            if (secondAllot.UseTempId != null) tempId = (int)secondAllot.UseTempId;

            var header = GetHeadItems(hospitalId, tempId, secondAllot);

            var body = GetBodyItems(userId, employeeSource, secondAllot, prevSecondAllot, header, savedDataList, isArchive);

            var result = new SecondResponse { HeadItems = header, BodyItems = body };

            SupplyHeaderByWorkItem(hospitalId, result, secondAllot, savedDataList);

            result.HeadItems = result.HeadItems.OrderBy(t => t.Type).ThenBy(t => t.Sort).ThenBy(t => t.FiledName).ToList();
            result.BodyItems = result.BodyItems.OrderBy(t => t.RowNumber).ToList();

            return result;
        }

        /// <summary>
        /// 获取显示列
        /// </summary>
        /// <param name="hospitalId"></param>
        /// <param name="tempId"></param>
        /// <param name="secondAllot"></param>
        /// <returns></returns>
        public List<HeadItem> GetHeadItems(int hospitalId, int tempId, ag_secondallot secondAllot)
        {
            // 数据库配置固定的列
            var fixedHeaders = agtempitemRepository.GetEntities(w => w.TempId == tempId);

            // 用户自定义的工作量、单项奖励
            var temp = agworkloadRepository.GetEntities(w => w.HospitalId == hospitalId);
            var configHeaders = temp.Where(w => UnitTypeUtil.Is(w.UnitType, secondAllot.UnitType) && w.Department == secondAllot.Department);
            if (configHeaders.Count() == 0)
                configHeaders = temp?.Where(w => UnitTypeUtil.Is(w.UnitType, secondAllot.NewUnitType) && w.Department == secondAllot.NewAccountingUnit)?.ToList() ?? new List<ag_workload>();

            if (SecondAllotService.defaultValues != null && SecondAllotService.defaultValues.Any())
                configHeaders = configHeaders?.Where(w => !SecondAllotService.defaultValues.Select(t => t.Item1).Contains(w.ItemName)).ToList();

            // 初始化固定列
            var headItems = _mapper.Map<List<HeadItem>>(fixedHeaders) ?? new List<HeadItem>();

            //获取工作量、单项奖励列
            if (configHeaders != null && configHeaders.Any())
            {
                foreach (var workTypeId in configHeaders.Select(t => t.WorkTypeId).Distinct())
                {
                    var workDtos = _mapper.Map<List<HeadItem>>(configHeaders.Where(t => t.WorkTypeId == workTypeId));
                    int type = workTypeId == (int)AgWorkloadType.SingleAwards
                        ? (int)TempColumnType.SingleAwardsColumns
                        : (int)TempColumnType.WorkloadColumns;

                    workDtos.ForEach(t => t.Type = type);
                    headItems.AddRange(workDtos);
                }
            }

            if (headItems != null && headItems.Any())
                headItems = headItems.OrderBy(s => s.Type).ThenBy(s => s.Sort).ToList();

            return headItems;
        }

        /// <summary>
        /// 获取显示的数据
        /// </summary>
        /// <param name="userId"></param>
        /// <param name="employeeSource"></param>
        /// <param name="secondAllot"></param>
        /// <param name="prevSecondAllot"></param>
        /// <param name="headItems"></param>
        /// <returns></returns>
        public List<BodyItem> GetBodyItems(int userId, int employeeSource, ag_secondallot secondAllot, ag_secondallot prevSecondAllot, List<HeadItem> headItems, List<ag_fixatitem> savedDataList, int isArchive)
        {
            var bodyItems = new List<BodyItem>();

            if (headItems == null || !headItems.Any()) return bodyItems;

            var otherShowColumns = headItems.Where(w => w.Type != (int)TempColumnType.TopFixedColumns)?.ToList();
            if (otherShowColumns != null && employeeSource == (int)EmployeeSource.Initial)
            {
                // 保存过数据，从保存的数据中心带出信息
                // 未保存过数据，带入初始数据(首次填写二次绩效，人员信息来自人员字典，有历史二次绩效记录时，人员信息来自上次二次绩效填写记录)
                if (savedDataList == null || !savedDataList.Any())
                {
                    employeeSource = prevSecondAllot == null ? (int)EmployeeSource.EmployeeDict : (int)EmployeeSource.PrevSecondAllot;
                }
            }

            if (isArchive == 1 || new List<int> { (int)SecondAllotStatus.WaitReview, (int)SecondAllotStatus.PassAudit }.Contains(secondAllot.Status ?? (int)SecondAllotStatus.Uncommitted))
                employeeSource = (int)EmployeeSource.Initial;

            var topFixedColumns = headItems.Where(w => w.Type == (int)TempColumnType.TopFixedColumns)?.ToList();
            if (topFixedColumns != null)
            {
                var topFixedDataList = new List<BodyItem>();
                foreach (var column in topFixedColumns)
                {
                    var topFixedData = new BodyItem(column);
                    var savedData = savedDataList?.FirstOrDefault(w => w.RowNumber == -1 && w.Type == (int)TempColumnType.TopFixedColumns && w.ItemName == column.FiledName);
                    if (savedData != null)
                    {
                        topFixedData.Value = savedData.ItemValue;
                    }
                    topFixedData.RowNumber = -1;
                    topFixedDataList.Add(topFixedData);
                }
                SupplementFixedData(secondAllot, topFixedDataList, prevSecondAllot, employeeSource);
                bodyItems.AddRange(topFixedDataList);
            }

            if (otherShowColumns != null)
            {
                var tableFixedDataList = GetBodyItemsByEmployeeSource(userId, employeeSource, secondAllot, prevSecondAllot, savedDataList, otherShowColumns);
                bodyItems.AddRange(tableFixedDataList);
            }

            return bodyItems;
        }

        private List<BodyItem> GetBodyItemsByEmployeeSource(int userId, int employeeSource, ag_secondallot secondAllot, ag_secondallot prevSecondAllot,
            List<ag_fixatitem> savedDataList, List<HeadItem> otherShowColumns)
        {
            switch (employeeSource)
            {
                case (int)EmployeeSource.Initial:
                    return GetEmployeeFromSavedData(userId, secondAllot, savedDataList, otherShowColumns);

                case (int)EmployeeSource.EmployeeDict:
                    return GetEmployeeFromEmployeeDict(userId, secondAllot, otherShowColumns);

                case int source when source == (int)EmployeeSource.PrevSecondAllot && prevSecondAllot != null:
                    return GetEmployeeFromPrevData(userId, secondAllot, prevSecondAllot, otherShowColumns);

                default:
                    return new List<BodyItem>();
            }
        }

        /// <summary>
        /// 从保存过的数据中获取人员信息
        /// </summary>
        /// <param name="savedDataList"></param>
        /// <param name="otherShowColumns"></param>
        /// <returns></returns>
        public List<BodyItem> GetEmployeeFromSavedData(int userId, ag_secondallot secondAllot, List<ag_fixatitem> savedDataList, List<HeadItem> otherShowColumns)
        {
            var tableFixedDataList = new List<BodyItem>();

            if (otherShowColumns == null || savedDataList == null || !savedDataList.Any(w => w.RowNumber.HasValue && w.RowNumber > -1))
                return tableFixedDataList;

            var rowNumberList = savedDataList.Where(w => w.RowNumber.HasValue && w.RowNumber > -1)?.Select(w => w.RowNumber.Value).Distinct().OrderBy(t => t).ToList();
            if (rowNumberList != null && rowNumberList.Any())
            {
                var employeeList = personService.GetPersons(secondAllot.AllotId.Value, userId);
                foreach (var rowNumber in rowNumberList)
                {
                    foreach (var column in otherShowColumns)
                    {
                        var tableFixedData = new BodyItem(column);
                        var savedData = savedDataList.FirstOrDefault(w => w.RowNumber == rowNumber && w.Type == column.Type && w.ItemName == column.FiledName);
                        if (savedData != null)
                        {
                            tableFixedData.Value = savedData.ItemValue;
                        }
                        tableFixedData.RowNumber = rowNumber;
                        tableFixedDataList.Add(tableFixedData);
                    }
                }
                SupplementOtherPerfor(secondAllot, tableFixedDataList, employeeList, otherShowColumns);
            }
            return tableFixedDataList;
        }

        /// <summary>
        /// 从人员字典中获取人员信息
        /// </summary>
        /// <param name="userId"></param>
        /// <param name="secondAllot"></param>
        /// <param name="otherShowColumns"></param>
        /// <returns></returns>
        public List<BodyItem> GetEmployeeFromEmployeeDict(int userId, ag_secondallot secondAllot, List<HeadItem> otherShowColumns)
        {
            var tableFixedDataList = new List<BodyItem>();

            if (otherShowColumns == null || !otherShowColumns.Any()) return tableFixedDataList;

            var employeeList = personService.GetPersons(secondAllot.AllotId.Value, userId);
            if (employeeList == null || !employeeList.Any()) return tableFixedDataList;

            //var perapramounts = perapramountRepository.GetEntities(t => t.AllotId == secondAllot.AllotId && t.Status == 3);
            //Func<per_employee, decimal?> getAprAmount = (t) => perapramounts
            //    ?.Where(w => w.AccountingUnit?.Trim() == secondAllot.Department?.Trim() && w.DoctorName?.Trim() == t.DoctorName?.Trim() && w.PersonnelNumber?.Trim() == t.JobNumber?.Trim())
            //    ?.Sum(w => w.Amount);

            var employeeColumns = new List<Tuple<string, string, Func<per_employee, object>>>
            {
                new Tuple<string, string, Func<per_employee, object>>("人员工号", "PersonnelNumber", (t) => t.PersonnelNumber),
                new Tuple<string, string, Func<per_employee, object>>("姓名", "FullName", (t) => t.DoctorName),
                new Tuple<string, string, Func<per_employee, object>>(
                    "岗位", "Post",
                    (t) => (t.Duty?.IndexOf("主任") > -1 || t.Duty?.IndexOf("护士长") > -1) ? "科主任/护士长" : "其他"),
                new Tuple<string, string, Func<per_employee, object>>("出勤", "ActualAttendance", (t) => t.AttendanceDay),
                new Tuple<string, string, Func<per_employee, object>>("人员系数", "StaffCoefficient", (t) => 1),
                new Tuple<string, string, Func<per_employee, object>>("职称", "JobTitle", (t) => t.JobTitle),
                new Tuple<string, string, Func<per_employee, object>>("预留年度考核比例", "ReservedRatio", (t) => t.ReservedRatio),
                new Tuple<string, string, Func<per_employee, object>>("医院其他绩效", "OtherPerformance", (t) => 0)
            };

            int rowNumber = 0;
            foreach (var employee in employeeList)
            {
                foreach (var column in employeeColumns)
                {
                    var headItem = otherShowColumns.FirstOrDefault(w => w.FiledName == column.Item1 && w.FiledId == column.Item2 && w.Type == (int)TempColumnType.TableFixedColumns);
                    if (headItem == null) continue;

                    var tableFixedData = new BodyItem(headItem);

                    var value = column.Item3.Invoke(employee);
                    tableFixedData.Value = value?.ToString();
                    tableFixedData.RowNumber = rowNumber;
                    tableFixedDataList.Add(tableFixedData);
                }
                rowNumber++;
            }
            SupplementOtherPerfor(secondAllot, tableFixedDataList, employeeList, otherShowColumns);
            return tableFixedDataList;
        }

        /// <summary>
        /// 从上一次的保存信息中获取人员信息
        /// </summary>
        /// <param name="prevSecondAllot"></param>
        /// <param name="otherShowColumns"></param>
        /// <returns></returns>
        public List<BodyItem> GetEmployeeFromPrevData(int userId, ag_secondallot secondAllot, ag_secondallot prevSecondAllot, List<HeadItem> otherShowColumns)
        {
            var tableFixedDataList = new List<BodyItem>();

            if (prevSecondAllot == null || otherShowColumns == null || !otherShowColumns.Any()) return tableFixedDataList;

            var savedDataList = agfixatitemRepository.GetEntities(w => w.SecondId == prevSecondAllot.Id && w.RowNumber.HasValue && w.RowNumber > -1);
            if (savedDataList == null || !savedDataList.Any()) return tableFixedDataList;

            // groupby取最后的一条记录，避免重复数据
            savedDataList = savedDataList.GroupBy(t => new { t.RowNumber, t.ItemName, t.Sort })
                .Select(t => t.OrderByDescending(o => o.ID).FirstOrDefault()).ToList();

            var employeeList = personService.GetPersons(secondAllot.AllotId.Value, userId);
            var employeeColumns = new List<Tuple<string, string>>
            {
                new Tuple<string, string>("人员工号", "PersonnelNumber"),
                new Tuple<string, string>("姓名", "FullName"),
                new Tuple<string, string>("岗位", "Post"),
                new Tuple<string, string>("人员系数", "StaffCoefficient"),
                new Tuple<string, string>("职称", "JobTitle"),
                new Tuple<string, string>("职称系数", "TitleCoefficient")
            };

            var rowNumberList = savedDataList.Select(w => w.RowNumber.Value).Distinct().OrderBy(t => t).ToList();
            if (rowNumberList != null && rowNumberList.Any())
            {
                foreach (var rowNumber in rowNumberList)
                {
                    foreach (var column in employeeColumns)
                    {
                        var headItem = otherShowColumns.FirstOrDefault(w => w.FiledName == column.Item1 && w.FiledId == column.Item2 && w.Type == (int)TempColumnType.TableFixedColumns);
                        if (headItem == null) continue;

                        var tableFixedData = new BodyItem(headItem);

                        var savedData = savedDataList.FirstOrDefault(w => w.RowNumber == rowNumber && w.Type == (int)TempColumnType.TableFixedColumns && w.ItemName == column.Item1);
                        if (savedData != null)
                        {
                            tableFixedData.Value = savedData.ItemValue;
                        }
                        tableFixedData.RowNumber = rowNumber;
                        tableFixedDataList.Add(tableFixedData);
                    }

                    #region 获取人员字典中录入的出勤

                    var attenItem = otherShowColumns.FirstOrDefault(w => w.FiledName == "出勤" && w.FiledId == "ActualAttendance" && w.Type == (int)TempColumnType.TableFixedColumns);
                    if (attenItem == null) continue;
                    var attendance = new BodyItem(attenItem);

                    var personnelNumber = savedDataList.FirstOrDefault(w => w.RowNumber == rowNumber && w.ItemName == "人员工号")?.ItemValue;
                    var personName = savedDataList.FirstOrDefault(w => w.RowNumber == rowNumber && w.ItemName == "姓名")?.ItemValue;
                    var employeeAttendance = employeeList.FirstOrDefault(w => w.PersonnelNumber == personnelNumber && w.DoctorName == personName)?.AttendanceDay.ToString();

                    attendance.Value = employeeAttendance;
                    attendance.RowNumber = rowNumber;
                    tableFixedDataList.Add(attendance);

                    #endregion 获取人员字典中录入的出勤
                }

                SupplementOtherPerfor(secondAllot, tableFixedDataList, employeeList, otherShowColumns);
            }
            return tableFixedDataList;
        }

        /// <summary>
        /// 补充顶部数据中的固定信息
        /// </summary>
        /// <param name="secondAllot"></param>
        /// <param name="bodyItems"></param>
        private void SupplementFixedData(ag_secondallot secondAllot, List<BodyItem> bodyItems, ag_secondallot prevSecondAllot, int employeeSource)
        {
            if (bodyItems == null || !bodyItems.Any(w => w.RowNumber == -1)) return;

            var keyValue = new Dictionary<string, string>
            {
                { "发放月份", $"{secondAllot.Year}年{secondAllot.Month.ToString().PadLeft(2, '0')}月" },
                //{ "可分配绩效", secondAllot.RealGiveFee.ToString() },
            };

            if (new int[] { 1, 4 }.Contains(secondAllot.Status ?? 0))
                keyValue.Add("可分配绩效", secondAllot.RealGiveFee.ToString());

            var pairs = new Dictionary<string, string>
            {
                { "职称绩效", "年资职称绩效占比" },
                { "工作量绩效", "工作量绩效占比" },
            };

            if (employeeSource == (int)EmployeeSource.PrevSecondAllot && prevSecondAllot != null)
            {
                var savedDataList = agfixatitemRepository.GetEntities(w => w.SecondId == prevSecondAllot.Id && w.RowNumber.HasValue && w.RowNumber == -1);
                if (savedDataList != null && savedDataList.Any())
                {
                    foreach (var item in pairs)
                    {
                        var savedData = savedDataList.FirstOrDefault(w => w.Type == (int)TempColumnType.TopFixedColumns && w.ItemName == item.Value);
                        if (!string.IsNullOrEmpty(savedData?.ItemValue) && !keyValue.Keys.Contains(item.Value))
                            keyValue.Add(item.Value, savedData.ItemValue);
                    }
                }
            }
            else if (employeeSource == (int)EmployeeSource.Initial)
            {
                var savedDataList = agfixatitemRepository.GetEntities(w => w.SecondId == secondAllot.Id && w.RowNumber.HasValue && w.RowNumber == -1);
                if (savedDataList != null && savedDataList.Any())
                {
                    foreach (var item in pairs)
                    {
                        var savedData = savedDataList.FirstOrDefault(w => w.Type == (int)TempColumnType.TopFixedColumns && w.ItemName == item.Value);
                        if (!string.IsNullOrEmpty(savedData?.ItemValue) && !keyValue.Keys.Contains(item.Value))
                            keyValue.Add(item.Value, savedData.ItemValue);
                    }
                }
            }
            else
            {
                var configs = cofagainRepository.GetEntities(t => t.AllotID == secondAllot.AllotId);
                if (configs != null && configs.Any())
                {
                    foreach (var config in configs)
                    {
                        var key = pairs.ContainsKey(config.TypeName) ? pairs[config.TypeName] : config.TypeName;
                        if (!keyValue.Keys.Contains(key))
                            keyValue.Add(key, config.Value.ToString());
                    }
                }
            }

            foreach (var item in keyValue)
            {
                var field = bodyItems.FirstOrDefault(w => w.RowNumber == -1 && w.FiledName == item.Key);
                if (field != null && !string.IsNullOrEmpty(item.Value))
                    field.Value = item.Value;
            }

            var days = bodyItems.FirstOrDefault(w => w.RowNumber == -1 && w.FiledName == "满勤天数");
            if (days != null && string.IsNullOrEmpty(days.Value))
                days.Value = secondAllot.Year.Value >= 1 && secondAllot.Month.Value <= 12 ? DateTime.DaysInMonth(secondAllot.Year.Value, secondAllot.Month.Value).ToString() : "30";
            ;
        }

        /// <summary>
        /// 补充 医院其他绩效
        /// </summary>
        /// <param name="result"></param>
        private void SupplementOtherPerfor(ag_secondallot secondAllot, List<BodyItem> bodyItems, List<per_employee> employeeList, List<HeadItem> otherShowColumns)
        {
            if (bodyItems == null || !bodyItems.Any(w => w.RowNumber > -1)) return;

            var perapramounts = perapramountRepository.GetFullAmount(secondAllot.AllotId, 3);
            if (perapramounts == null || !perapramounts.Any()) return;

            var rowNumberList = bodyItems.Where(w => w.RowNumber > -1).Select(w => w.RowNumber).Distinct().OrderBy(t => t).ToList();
            foreach (var rownum in rowNumberList)
            {
                var rowData = bodyItems.Where(w => w.RowNumber == rownum);
                var personnelNumber = rowData.FirstOrDefault(w => w.FiledId == "PersonnelNumber")?.Value;
                var fullName = rowData.FirstOrDefault(w => w.FiledId == "FullName")?.Value;

                var employee = employeeList.FirstOrDefault(w => w.PersonnelNumber == personnelNumber);
                if (employee == null) continue;

                var hasAmountData = perapramounts?.Where(w => w.PersonnelNumber?.Trim() == personnelNumber?.Trim());
                if (hasAmountData == null || !hasAmountData.Any()) continue;

                var amount = secondAllot.Department == employee.AccountingUnit ? hasAmountData.Sum(w => w.Amount) : 0;

                perapramounts.RemoveAll(w => w.PersonnelNumber?.Trim() == personnelNumber?.Trim());

                var otherPerfor = rowData.FirstOrDefault(w => w.FiledId == "OtherPerformance");
                if (otherPerfor != null)
                    otherPerfor.Value = amount?.ToString();
            }

            // 补充字典中该科室不存在，但有其它绩效的人员信息
            if (perapramounts != null && perapramounts.Any(t => t.AccountingUnit == secondAllot.Department))
            {
                var groupData = perapramounts.Where(t => t.AccountingUnit == secondAllot.Department).GroupBy(t => t.PersonnelNumber)
                    .Select(t => new
                    {
                        PersonnelNumber = t.Key,
                        DoctorName = t.FirstOrDefault(w => !string.IsNullOrEmpty(w.DoctorName))?.DoctorName,
                        Amount = t.Sum(w => w.Amount)?.ToString()
                    });

                var lastNumber = rowNumberList.Max() + 1;

                var employeeColumns = new List<Tuple<string, string, Func<per_employee, object>>>
                {
                    new Tuple<string, string, Func<per_employee, object>>("人员工号", "PersonnelNumber", (t) => t.PersonnelNumber),
                    new Tuple<string, string, Func<per_employee, object>>("姓名", "FullName", (t) => t.DoctorName),
                    new Tuple<string, string, Func<per_employee, object>>(
                        "岗位", "Post",
                        (t) => (t.Duty?.IndexOf("主任") > -1 || t.Duty?.IndexOf("护士长") > -1) ? "科主任/护士长" : "其他"),
                    new Tuple<string, string, Func<per_employee, object>>("出勤", "ActualAttendance", (t) => t.AttendanceDay),
                    new Tuple<string, string, Func<per_employee, object>>("人员系数", "StaffCoefficient", (t) => 1),
                    new Tuple<string, string, Func<per_employee, object>>("职称", "JobTitle", (t) => t.JobTitle),
                    new Tuple<string, string, Func<per_employee, object>>("预留年度考核比例", "ReservedRatio", (t) => t.ReservedRatio),
                    new Tuple<string, string, Func<per_employee, object>>("医院其他绩效", "OtherPerformance", (t) => 0),
                    new Tuple<string, string, Func<per_employee, object>>("实发绩效工资金额", "ShifaAmountOfPerformancePay", (t) => 0)
                };

                var specialColumns = new string[] { "FullName", "OtherPerformance", "ShifaAmountOfPerformancePay" };

                foreach (var item in groupData)
                {
                    foreach (var column in employeeColumns)
                    {
                        if (employeeList.Any(t => t.PersonnelNumber == item.PersonnelNumber)
                        && employeeList.FirstOrDefault(t => t.PersonnelNumber == item.PersonnelNumber) is per_employee employee
                        && UnitTypeUtil.IsEqualsUnitType(employee.UnitType, secondAllot.UnitType))
                        {
                            var headItem = otherShowColumns.FirstOrDefault(w => w.FiledName == column.Item1 && w.FiledId == column.Item2 && w.Type == (int)TempColumnType.TableFixedColumns);
                            if (headItem == null) continue;

                            var data = new BodyItem(headItem);
                            data.RowNumber = lastNumber;
                            if (column.Item2 == "FullName")
                                data.Value = item.DoctorName;
                            else if (column.Item2 == "OtherPerformance")
                                data.Value = item.Amount;
                            else if (column.Item2 == "ShifaAmountOfPerformancePay")
                                data.Value = item.Amount;
                            else
                            {
                                var value = column.Item3.Invoke(employee);
                                data.Value = value?.ToString();
                            }
                            bodyItems.Add(data);
                        }
                    }
                    lastNumber++;
                }
            }
        }

        /// <summary>
        /// 根绝添加工作量类型判断是否添加
        /// </summary>
        /// <param name="hospitalId"></param>
        /// <param name="result"></param>
        /// <param name="secondAllot"></param>
        /// <param name="fixatitems"></param>
        private void SupplyHeaderByWorkItem(int hospitalId, SecondResponse result, ag_secondallot secondAllot, List<ag_fixatitem> fixatitems)
        {
            //不包含工作量绩效
            if (!result.HeadItems.Select(t => t.FiledId).Contains("PerformanceShareTheWorkload")) return;

            var maxSortValue = result.HeadItems.Where(t => t.Type == 1).Max(t => t.Sort);

            var headers = new HeadItem[]
            {
                new HeadItem { FiledId = "ThePerformanceOf", Type =  1, SourceType = 1, IsBring =  2, SpecialAttr = 1 },
                new HeadItem { FiledId = "ThePerformanceAmountOf", Type =  1, SourceType = 1, IsBring =  2, SpecialAttr = 2 }
            };

            var headerItems = new List<HeadItem>();

            var unit = secondAllot.UnitType == UnitType.医技组.ToString() ? UnitType.医生组.ToString() : secondAllot.UnitType;
            var temp = agworkloadtypeRepository.GetEntities(t => t.HospitalId == hospitalId);
            var deptHeader = temp.Where(w => UnitTypeUtil.Is(w.UnitType, unit) && w.Department == secondAllot.Department) ?? new List<ag_workload_type>();
            if (deptHeader.Count() == 0)
                deptHeader = temp?.Where(w => UnitTypeUtil.Is(w.UnitType, secondAllot.NewUnitType) && w.Department == secondAllot.NewAccountingUnit) ?? new List<ag_workload_type>();


            if (deptHeader != null && deptHeader.Any())
            {
                int sortindex = 1;
                foreach (var item in deptHeader)
                {
                    if (item.HospitalId == 0) continue;

                    for (int i = 0; i < headers.Length; i++)
                    {
                        var headItem = (HeadItem)headers[i].Clone();
                        headItem.FiledName = (i % 2 == 0) ? item.TypeName : item.TypeName.Replace("占比", "金额");
                        if (i % 2 != 0 && !headItem.FiledName.EndsWith("金额"))
                            headItem.FiledName += "金额";
                        headItem.FiledId += item.Id;
                        headItem.Sort = maxSortValue + sortindex;
                        headerItems.Add(headItem);
                        sortindex++;
                    }
                }
            }
            var defauleHeader = new List<ag_workload_type>
            {
                new ag_workload_type { Id = (int) AgWorkloadType.Workload, TypeName = "工作量绩效占比", },
                new ag_workload_type { Id = (int) AgWorkloadType.Workload, TypeName = "工作量分配绩效金额" },
            };
            foreach (var item in defauleHeader)
            {
                result.HeadItems.Where(t => t.FiledName == item.TypeName).ToList()?.ForEach(t =>
                {
                    t.SpecialAttr = item.TypeName.IndexOf("占比") > -1 ? 1 : 2;
                });
                result.BodyItems.Where(t => t.FiledName == item.TypeName).ToList()?.ForEach(t =>
                {
                    t.SpecialAttr = item.TypeName.IndexOf("占比") > -1 ? 1 : 2;
                });
            }

            var rownumber = result.BodyItems.Any(t => t.RowNumber == -1) ? -1 : 0;
            foreach (var item in headerItems)
            {
                if (!result.HeadItems.Select(t => t.FiledId).Contains(item.FiledId))
                {
                    result.HeadItems.Add(item);
                    var body = _mapper.Map<BodyItem>(item);
                    body.RowNumber = rownumber;
                    if (fixatitems != null && fixatitems.Any(t => t.ItemName == item.FiledName))
                        body.Value = fixatitems.FirstOrDefault(t => t.ItemName == item.FiledName).ItemValue;
                    result.BodyItems.Add(body);
                }
            }
        }

        #endregion 横向纵向模板详情

        #region 其他模板详情

        public HandsonTableBase GetOtherTempData(int userId, int secondId, int isArchive, int employeeSource, out decimal? realAmount)
        {
            string[] workNumbers = new string[] { };
            var details = GetOtherTempDetails(userId, secondId, isArchive, employeeSource);

            var secondAllot = agsecondallotRepository.GetEntity(t => t.Id == secondId);


            var readColumns = new int[] { 2, 3, }.Contains(secondAllot.Status.Value) ?
                OtherTemp.Select(t => t.Value).ToArray() :
                new string[] { "可分配绩效", "科室单项奖励", "医院其他绩效", "预留年度考核比例", "年度考核发放金额", "预发绩效工资金额" };

            HandsonTableBase result = new HandsonTableBase()
            {
                Columns = OtherTemp.Select(t => new HandsonColumn(t.Value)).ToList(),
                ColHeaders = OtherTemp.Select(t => t.Value).ToList(),
            };

            if (result.Columns != null && result.Columns.Any())
            {
                foreach (var column in result.Columns)
                {
                    if (column.Data == "工号")
                    {
                        column.Type = "autocomplete";
                        column.Source = workNumbers;
                        column.Strict = true;
                    }
                }
            }
            realAmount = details?.Sum(t => t.RealAmount);
            if (details == null || !details.Any()) return result;

            details.ForEach(t =>
            {
                if (!t.OtherPerformance.HasValue) t.OtherPerformance = 0;
            });

            List<HandsonRowData> rowDatas = new List<HandsonRowData>();
            int i = 1;
            foreach (var item in details)
            {
                var json = JsonHelper.Serialize(item);
                var firstDic = JsonHelper.Deserialize<Dictionary<string, string>>(json);

                var cells = (from conf in OtherTemp join fst in firstDic on conf.Key.ToUpper() equals fst.Key.ToUpper() select new HandsonCellData(conf.Value, fst.Value)).ToList();

                cells.Add(new HandsonCellData(nameof(ag_othersource.Id), item.Id));

                rowDatas.Add(new HandsonRowData(i, cells));
                i++;
            }
            result.SetRowData(rowDatas);
            return result;
        }

        public List<ag_othersource> GetOtherTempDetails(int userId, int secondId, int isArchive, int employeeSource)
        {
            var secondAllot = agsecondallotRepository.GetEntity(t => t.Id == secondId);
            if (secondAllot == null) throw new PerformanceException("二次绩效信息无效！");

            var allot = perallotRepository.GetEntity(w => w.ID == secondAllot.AllotId);
            if (allot == null) throw new PerformanceException("未查询到匹配的绩效信息");

            var prevSecondAllot = GetPreviousSecondAllot(allot.HospitalId, secondAllot);

            var result = new List<ag_othersource>();

            var savedDataList = agothersourceRepository.GetEntities(t => t.SecondId == secondId);

            var isSupplementTitlePerformance = savedDataList == null || !savedDataList.Any();

            if (employeeSource == (int)EmployeeSource.Initial && isSupplementTitlePerformance)
            {
                employeeSource = prevSecondAllot == null ? (int)EmployeeSource.EmployeeDict : (int)EmployeeSource.PrevSecondAllot;
            }

            if (isArchive == 1 || new List<int> { (int)SecondAllotStatus.WaitReview, (int)SecondAllotStatus.PassAudit }.Contains(secondAllot.Status ?? (int)SecondAllotStatus.Uncommitted))
                employeeSource = (int)EmployeeSource.Initial;

            var employees = personService.GetPersons(secondAllot.AllotId.Value, userId)?.Where(t => UnitTypeUtil.IsEqualsUnitType(t.UnitType, secondAllot.UnitType)).ToList();

            var role = _userService.GetUserFirstRole(userId);
            //行政科室 只有两种逻辑，或从保存中加载，或加载EXCEL表
            if (role?.Type == (int)Role.行政科室)
            {
                // 如果已经保存>>走保存加载逻辑
                if (employeeSource == (int)EmployeeSource.Initial)
                {
                    result = savedDataList.OrderBy(t => t.WorkNumber).ToList();
                }
                // 如果没保存>>走加载im_employee_logistics人员名单逻辑
                else
                {
                    var types = new string[] { "行政后勤", "行政工勤" };
                    var logistics = _imemployeelogisticsRepository.GetEntities(w => w.AllotID == secondAllot.AllotId && types.Contains(w.AccountType) && w.AccountingUnit == secondAllot.Department);
                    result = (logistics ?? new List<im_employee_logistics>())
                        .OrderBy(t => ConvertHelper.To<Int64>(t.PersonnelNumber, 0)).Select(w => new ag_othersource
                        {
                            SecondId = secondId,
                            WorkNumber = w.PersonnelNumber,
                            Name = w.DoctorName,
                            Department = w.AccountingUnit,
                            WorkPost = w.JobTitle,
                        }).ToDistinct().ToList();
                }
            }
            //非行政科室 有三种逻辑，或从保存中加载，或从人员字典加载，或从上次绩效中加载
            else
            {
                switch (employeeSource)
                {
                    case (int)EmployeeSource.Initial:
                        result = savedDataList.OrderBy(t => t.WorkNumber).ToList();
                        break;

                    case (int)EmployeeSource.EmployeeDict:
                        if (employees == null || !employees.Any()) return new List<ag_othersource>();
                        result = employees.OrderBy(t => t.PersonnelNumber).Select(t => new ag_othersource
                        {
                            SecondId = secondId,
                            WorkNumber = t.PersonnelNumber,
                            Name = t.DoctorName,
                            Department = t.Department,
                            WorkPost = t.JobTitle,
                        }).ToList();
                        break;

                    case int source when source == (int)EmployeeSource.PrevSecondAllot && prevSecondAllot != null:
                        var prevSavedDataList = agothersourceRepository.GetEntities(t => t.SecondId == prevSecondAllot.Id);
                        isSupplementTitlePerformance = prevSavedDataList == null || !prevSavedDataList.Any();
                        if (prevSavedDataList != null && prevSavedDataList.Any())
                        {
                            result = prevSavedDataList.OrderBy(t => t.WorkNumber)
                                .Select(t => new ag_othersource
                                {
                                    SecondId = secondId,
                                    WorkNumber = t.WorkNumber,
                                    Name = t.Name,
                                    Department = t.Department,
                                    WorkPost = t.WorkPost,
                                }).ToList();
                        }
                        break;
                }
            }
            var originalEmployees = personService.GetPerEmployee(secondAllot.AllotId.Value);
            SupplementSecondDetail(secondAllot, originalEmployees, result, isSupplementTitlePerformance);

            return result;
        }

        /// <summary>
        /// 补充二次分配 人员明细
        /// </summary>
        /// <param name="second"></param>
        /// <param name="employees"></param>
        /// <param name="result"></param>
        /// <param name="isTitlePerformance">是否补全职称绩效</param>
        private void SupplementSecondDetail(ag_secondallot second, List<per_employee> employees, List<ag_othersource> result, bool isTitlePerformance = true)
        {
            // 补充医院其他绩效  及  预留比例
            var perapramounts = perapramountRepository.GetFullAmount(second.AllotId, 3);
            var distPerformance = rescomputeRepository.GetEntities(t => t.AllotID == second.AllotId);

            foreach (var item in result)
            {
                var empl = employees?.FirstOrDefault(w => w.PersonnelNumber?.Trim() == item.WorkNumber?.Trim());
                item.ReservedRatio = empl?.ReservedRatio ?? 0;
                // 如果是行政后勤，则把绩效放在工作量上
                if (isTitlePerformance && UnitTypeUtil.IsOffice(second.UnitType))
                {
                    item.WorkPerformance = distPerformance?.Where(w => w.AccountingUnit == item.Department && w.JobNumber?.Trim() == item.WorkNumber?.Trim())?.Sum(w => w.GiveFee);
                    if (string.IsNullOrEmpty(item.WorkNumber))
                        item.WorkPerformance = distPerformance?.Where(w => w.AccountingUnit == item.Department && w.JobNumber?.Trim() == item.WorkNumber?.Trim())?.Sum(w => w.GiveFee);
                }
                // 必须是相同核算单元类型才能带出医院其他绩效
                if (second.UnitType.Replace("其他", "").Replace("医技", "医生")
                    == (empl?.UnitType ?? "").Replace("其他", "").Replace("医技", "医生"))
                {
                    if (UnitTypeUtil.IsOffice(second.UnitType))
                    {
                        if (string.IsNullOrEmpty(item.WorkNumber))
                        {
                            var hasAmountData = perapramounts?.Where(w => w.AccountingUnit == second.Department && w.PersonnelNumber?.Trim() == item.WorkNumber?.Trim());
                            if (hasAmountData == null || !hasAmountData.Any()) continue;
                            item.OtherPerformance = hasAmountData.Sum(w => w.Amount);
                            perapramounts.RemoveAll(w => w.AccountingUnit == second.Department && w.PersonnelNumber?.Trim() == item.WorkNumber?.Trim());
                        }
                        else
                        {
                            var hasAmountData = perapramounts?.Where(w => w.AccountingUnit == second.Department && w.PersonnelNumber?.Trim() == item.WorkNumber?.Trim());
                            if (hasAmountData == null || !hasAmountData.Any()) continue;
                            item.OtherPerformance = hasAmountData.Sum(w => w.Amount);
                            perapramounts.RemoveAll(w => w.AccountingUnit == second.Department && w.PersonnelNumber?.Trim() == item.WorkNumber?.Trim());
                        }
                    }
                    else if (!string.IsNullOrEmpty(empl?.AccountingUnit))
                    {
                        if (string.IsNullOrEmpty(item.WorkNumber))
                        {
                            var hasAmountData = perapramounts?.Where(w => w.AccountingUnit == second.Department && w.PersonnelNumber?.Trim() == item.WorkNumber?.Trim());
                            if (hasAmountData == null || !hasAmountData.Any()) continue;
                            item.OtherPerformance = hasAmountData.Sum(w => w.Amount);
                            perapramounts.RemoveAll(w => w.AccountingUnit == second.Department && w.PersonnelNumber?.Trim() == item.WorkNumber?.Trim());
                        }
                        else
                        {
                            var hasAmountData = perapramounts?.Where(w => w.AccountingUnit == second.Department && w.PersonnelNumber?.Trim() == item.WorkNumber?.Trim());
                            if (hasAmountData == null || !hasAmountData.Any()) continue;
                            item.OtherPerformance = hasAmountData.Sum(w => w.Amount);
                            perapramounts.RemoveAll(w => w.AccountingUnit == second.Department && w.PersonnelNumber?.Trim() == item.WorkNumber?.Trim());

                        }
                    }
                }
            }


            // 补充字典中该科室不存在，但有其它绩效的人员信息
            if (perapramounts != null && perapramounts.Any(t => t.AccountingUnit == second.Department))
            {
                var groupData = perapramounts.Where(t => t.AccountingUnit == second.Department).GroupBy(t => t.PersonnelNumber)
                    .Select(t => new ag_othersource
                    {
                        SecondId = second.Id,
                        WorkNumber = t.Key,
                        Name = t.FirstOrDefault(w => !string.IsNullOrEmpty(w.DoctorName))?.DoctorName,
                        OtherPerformance = t.Sum(w => w.Amount)
                    });

                foreach (var item in groupData)
                {
                    if (employees != null && employees.Any(t => t.PersonnelNumber == item.WorkNumber)
                        && employees.FirstOrDefault(t => t.PersonnelNumber == item.WorkNumber) is per_employee employee
                        && UnitTypeUtil.IsEqualsUnitType(employee.UnitType, second.UnitType))
                    {
                        item.ReservedRatio = employee.ReservedRatio;
                        item.Department = employee.AccountingUnit;
                        item.WorkPost = employee.JobTitle;
                        result.Add(item);
                    }
                }

            }
        }

        public static Dictionary<string, string> OtherTemp { get; } = new Dictionary<string, string>
        {
            { nameof (ag_othersource.WorkNumber), "工号" },
            { nameof (ag_othersource.Name), "姓名" },
            { nameof (ag_othersource.Department), "科室" },
            { nameof (ag_othersource.WorkPost), "职称" },
            { nameof (ag_othersource.TitlePerformance), "职称绩效" },
            { nameof (ag_othersource.WorkPerformance), "工作量绩效工资" },
            { nameof (ag_othersource.ManagementAllowance), "管理津贴" },
            { nameof (ag_othersource.IndividualReward), "单项奖励" },
            { nameof (ag_othersource.AllocationOfKeySpecialty), "重点专科分配" },
            { nameof (ag_othersource.DeptReward), "科室单项奖励" },
            { nameof (ag_othersource.DistPerformance), "可分配绩效" },
            { nameof (ag_othersource.OtherPerformance), "医院其他绩效" },
            { nameof (ag_othersource.NightWorkPerformance), "夜班工作量绩效" },
            { nameof (ag_othersource.ReservedRatio), "预留年度考核比例" },
            { nameof (ag_othersource.ReservedAmount), "年度考核发放金额" },
            { nameof (ag_othersource.RealAmount), "预发绩效工资金额" },
        };

        #endregion 其他模板详情

        #region Common

        /// <summary>
        /// 获取上一次的二次绩效
        /// </summary>
        /// <param name="hospitalId"></param>
        /// <param name="secondAllot"></param>
        /// <returns></returns>
        public ag_secondallot GetPreviousSecondAllot(int hospitalId, ag_secondallot secondAllot)
        {
            // 历史删除绩效时，未删除对应的二次绩效记录
            var allotList = perallotRepository.GetEntities(w => w.HospitalId == hospitalId)?.OrderBy(s => s.Year).ThenBy(s => s.Month).ToList();
            if (allotList == null || !allotList.Any()) throw new PerformanceException("未查询到符合的绩效记录");

            var allot = allotList.FirstOrDefault(w => w.ID == secondAllot.AllotId);
            if (allot == null) throw new PerformanceException("未查询到符合的绩效记录");

            var index = allotList.IndexOf(allot);
            if (index == 0) return null;

            var prevAllot = allotList[index - 1];
            var temp = agsecondallotRepository.GetEntities(w => w.AllotId == prevAllot.ID);

            var prevSecondAllot = temp.FirstOrDefault(w =>
                (UnitTypeUtil.IsEqualsUnitType(w.UnitType, secondAllot.UnitType) && w.Department == secondAllot.Department)
                || (UnitTypeUtil.IsEqualsUnitType(w.UnitType, secondAllot.NewUnitType) && w.Department == secondAllot.NewAccountingUnit));

            return prevSecondAllot;
        }

        #endregion Common
    }

    /// <summary>
    /// 二次绩效模板
    /// </summary>
    public enum Temp
    {
        /// <summary>
        /// 临床科室二次分配_其他来源
        /// </summary>
        other = 6,

        /// <summary>
        /// 二次分配_横向
        /// </summary>
        crosswise = 7,

        /// <summary>
        /// 二次分配_纵向
        /// </summary>
        lengthways = 8
    }

    /// <summary>
    /// 模板中显示项类型
    /// </summary>
    public enum TempColumnType
    {
        /// <summary>
        /// 顶部固定列
        /// </summary>
        TopFixedColumns = 1,

        /// <summary>
        /// 表格固定显示列
        /// </summary>
        TableFixedColumns = 2,

        /// <summary>
        /// 工作量配置列
        /// </summary>
        WorkloadColumns = 3,

        /// <summary>
        /// 单项奖励配置列
        /// </summary>
        SingleAwardsColumns = 4,
    }

    /// <summary>
    /// 人员信息来源
    /// </summary>
    public enum EmployeeSource
    {
        /// <summary>
        /// 初始化(用户保存后的数据)
        /// </summary>
        Initial = 0,

        /// <summary>
        /// 上一个二次绩效记录
        /// </summary>
        PrevSecondAllot = 1,

        /// <summary>
        /// 人员字典
        /// </summary>
        EmployeeDict = 2,

        /// <summary>
        /// 工作量
        /// </summary>
        Workload = 3
    }

    /// <summary>
    /// 二次绩效状态
    /// 1 未提交  2 等待审核 3 审核通过 4 驳回
    /// </summary>
    public enum SecondAllotStatus
    {
        Uncommitted = 1,
        WaitReview = 2,
        PassAudit = 3,
        Reject = 4
    }
}
