using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Performance.DtoModels;
using Performance.EntityModels;
using Performance.Infrastructure;
using Performance.Repository;
using System;
using System.Collections.Generic;
using System.Linq;

namespace Performance.Services
{
    public partial class SecondAllotService : IAutoInjection
    {
        #region 二次绩效详情

        private bool IsAudit(int? secondStatus)
        {
            return new int[] { 2, 3 }.Contains(secondStatus ?? 1);
        }

        public SecondAllotResponse GetSecondSavedData(int userId, int secondId, int employeeSource)
        {
            var second = agsecondallotRepository.GetEntity(t => t.Id == secondId);
            if (second == null) throw new PerformanceException("参数SecondId无效！");

            var allot = perallotRepository.GetEntity(t => t.ID == second.AllotId);
            if (allot == null) throw new PerformanceException("绩效记录不存在！");

            if (!IsAudit(second.Status))
                AddWorkTypeDefaultHeadValue(allot.HospitalId, second);

            var (tempId, name) = GetUsingTempId(allot.HospitalId, second);
            var detail = new Detail()
            {
                TempId = tempId,
                TempName = name,
                IsArchive = allot.States == 8 ? 1 : 0,
                States = allot.States,
                Status = second.Status ?? 1,
                Department = second.Department,
                UnitType = second.UnitType
            };

            SecondAllotResponse result = new SecondAllotResponse() { Detail = detail };

            var head = agheadsourceRepository.GetEntity(t => t.SecondId == secondId)
                ?? new ag_headsource
                {
                    SecondId = secondId,
                    TotalDistPerformance = second.RealGiveFee,
                    PaymentOfTheMonth = $"{allot.Year}年{allot.Month.ToString().PadLeft(2, '0')}月",
                    SeniorityTitlesAccountedPerformance = 0.2m,
                    Workload_Ratio_Default = 0.8m,
                    DaysFullAttendance = DateTime.DaysInMonth(allot.Year, allot.Month)
                };
            head.TotalDistPerformance = second.RealGiveFee;
            head.PaymentOfTheMonth = $"{allot.Year}年{allot.Month.ToString().PadLeft(2, '0')}月";
            JObject jObject = JObject.Parse(JsonConvert.SerializeObject(head));
            var headDynamic = agworktypesourceRepository.GetEntities(t => t.SecondId == secondId);
            if (headDynamic != null && headDynamic.Any())
            {
                foreach (var item in headDynamic.OrderBy(t => t.Id))
                    jObject.Add(new JProperty(item.FieldId, item.Value));
            }
            result.Head = jObject;

            var prevSecond = GetPreviousSecondAllot(allot.HospitalId, second);

            // 保存过数据，从保存的数据中心带出信息
            // 未保存过数据，带入初始数据(首次填写二次绩效，人员信息来自人员字典，有历史二次绩效记录时，人员信息来自上次二次绩效填写记录)
            if (head.Id == 0 && employeeSource == (int)EmployeeSource.Initial)
                employeeSource = prevSecond == null ? (int)EmployeeSource.EmployeeDict : (int)EmployeeSource.PrevSecondAllot;

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

            result.Body = GetBodyItemsByEmployeeSource(userId, employeeSource, second, prevSecond, allot);

            return result;
        }

        /// <summary>
        /// 根据不同情况获取不同数据
        /// </summary>
        /// <param name="userId"></param>
        /// <param name="employeeSource"></param>
        /// <param name="second"></param>
        /// <param name="prevSecond"></param>
        /// <param name="allot"></param>
        /// <returns></returns>
        private JArray GetBodyItemsByEmployeeSource(int userId, int employeeSource, ag_secondallot second, ag_secondallot prevSecond, per_allot allot)
        {
            List<ag_bodysource> bodysources = new List<ag_bodysource>();

            var employeeList = personService.GetPersons(allot.ID, userId);
            switch (employeeSource)
            {
                case (int)EmployeeSource.Initial:
                    bodysources = agbodysourceRepository.GetEntities(t => t.SecondId == second.Id);
                    break;

                case (int)EmployeeSource.EmployeeDict:
                    bodysources = GetEmployeeFromEmployeeDict(employeeList);
                    break;

                case int source when source == (int)EmployeeSource.PrevSecondAllot && prevSecond != null:
                    bodysources = GetEmployeeFromPrevData(prevSecond, employeeList, second.Id);
                    break;

                default:
                    bodysources = new List<ag_bodysource>();
                    break;
            }

            SupplementOtherPerfor(second, bodysources, employeeList);
            return IsAudit(second.Status) ? GetBodyResultAfterAudit(allot.HospitalId, second, bodysources) : GetBodyResult(allot.HospitalId, second, bodysources);
        }

        /// <summary>
        /// 从人员字典中获取人员信息
        /// </summary>
        /// <param name="employeeList"></param>
        /// <returns></returns>
        public List<ag_bodysource> GetEmployeeFromEmployeeDict(List<per_employee> employeeList)
        {
            if (employeeList == null || !employeeList.Any()) return new List<ag_bodysource>();

            List<ag_bodysource> bodysources = new List<ag_bodysource>();

            int rowNumber = 1;
            foreach (var employee in employeeList)
            {
                bodysources.Add(new ag_bodysource
                {
                    RowNumber = rowNumber,
                    WorkNumber = employee.PersonnelNumber,
                    Department = employee.AccountingUnit,
                    Name = employee.DoctorName,
                    Post = getPost.Invoke(employee),
                    ActualAttendance = employee.AttendanceDay,
                    StaffCoefficient = 1,
                    JobTitle = employee.JobTitle,
                    ReservedRatio = employee.ReservedRatio,
                    OtherPerformance = 0
                });
                rowNumber++;
            }
            return bodysources;
        }

        /// <summary>
        /// 从上一次的保存信息中获取人员信息
        /// </summary>
        /// <param name="prevSecond"></param>
        /// <param name="employeeList"></param>
        /// <param name="secondId"></param>
        /// <returns></returns>
        public List<ag_bodysource> GetEmployeeFromPrevData(ag_secondallot prevSecond, List<per_employee> employeeList, int secondId)
        {
            var result = new List<ag_bodysource>();
            var body = agbodysourceRepository.GetEntities(t => t.SecondId == prevSecond.Id);
            if (body == null || !body.Any()) return result;

            var data = body.Select(t => new ag_bodysource
            {
                Id = 0,
                SecondId = secondId,
                WorkNumber = t.WorkNumber,
                Name = t.Name,
                Post = t.Post,
                Department = t.Department,
                StaffCoefficient = t.StaffCoefficient,
                JobTitle = t.JobTitle,
                TitleCoefficient = t.TitleCoefficient
            });
            foreach (var item in data)
            {
                item.ActualAttendance = employeeList?.FirstOrDefault(w => w.PersonnelNumber == item.WorkNumber && w.DoctorName == item.Name)?.AttendanceDay;
                result.Add(item);
            }
            return result;
        }

        /// <summary>
        /// 补充医院其他绩效
        /// </summary>
        /// <param name="secondAllot"></param>
        /// <param name="bodyItems"></param>
        /// <param name="employeeList"></param>
        /// <param name="isSupplyOtherEmployees">是否补充字典中该科室不存在，但有其它绩效的人员信息</param>
        private void SupplementOtherPerfor(ag_secondallot secondAllot, List<ag_bodysource> bodyItems, List<per_employee> employeeList, bool isSupplyOtherEmployees = true)
        {
            if (bodyItems == null || !bodyItems.Any(w => w.RowNumber > -1)) return;

            var perapramounts = perapramountRepository.GetFullAmount(t => t.AllotId == secondAllot.AllotId && t.Status == 3);
            if (perapramounts == null || !perapramounts.Any()) return;

            foreach (var rowitem in bodyItems)
            {
                var employee = employeeList.FirstOrDefault(w => w.PersonnelNumber == rowitem.WorkNumber);
                if (employee == null) continue;

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

                rowitem.OtherPerformance = secondAllot.Department == employee.AccountingUnit && secondAllot.UnitType == employee.UnitType ? hasAmountData.Sum(w => w.Amount) : 0;

                perapramounts.RemoveAll(w => w.PersonnelNumber?.Trim() == rowitem.WorkNumber?.Trim());
            }

            // 补充字典中该科室不存在，但有其它绩效的人员信息
            if (isSupplyOtherEmployees && 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)
                    });

                // int maxRownumber = bodyItems.Max(t => t.RowNumber ?? 0) + 1;
                foreach (var item in groupData)
                {
                    var employee = employeeList.FirstOrDefault(w => w.PersonnelNumber == item.PersonnelNumber);
                    if (employee != null && employee.UnitType == secondAllot.UnitType)
                    {
                        bodyItems.Add(new ag_bodysource
                        {
                            WorkNumber = item.PersonnelNumber,
                            Name = item.DoctorName,
                            Post = getPost.Invoke(employee),
                            ActualAttendance = employee.AttendanceDay,
                            StaffCoefficient = 1,
                            JobTitle = employee.JobTitle,
                            ReservedRatio = employee.ReservedRatio,
                            OtherPerformance = item.Amount
                        });
                    }
                }
            }
        }

        /// <summary>
        /// 获取body的结果集
        /// </summary>
        /// <param name="hospitalId"></param>
        /// <param name="second"></param>
        /// <param name="bodysources"></param>
        /// <returns></returns>
        private JArray GetBodyResult(int hospitalId, ag_secondallot second, List<ag_bodysource> bodysources)
        {
            JArray jArray = new JArray();
            if (bodysources == null || !bodysources.Any())
                return jArray;

            var bodyDynamic = agworkloadsourceRepository.GetEntities(t => bodysources.Select(w => w.Id).Contains(t.BodyId));
            var workloads = agworkloadRepository.GetEntities(t => t.HospitalId == hospitalId && t.Department == second.Department && t.UnitType == second.UnitType)
                ?.OrderBy(t => t.WorkTypeId).ThenBy(t => t.Sort).ToList();
            if (workloads != null && workloads.Any())
            {
                foreach (var item in bodysources.OrderBy(t => t.Id))
                {
                    JObject jObj = JObject.Parse(JsonConvert.SerializeObject(item));    //使用jsonHelper会自动隐藏空值的项
                    foreach (var workitem in workloads)
                    {
                        var value = bodyDynamic?.FirstOrDefault(w => w.BodyId == item.Id && w.WorkloadId == workitem.Id)?.Value;
                        jObj.Add(new JProperty(workitem.ItemId, value));
                    }
                    jArray.Add(jObj);
                }
            }
            return jArray;
        }


        /// <summary>
        /// 获取审核通过后body的结果集
        /// </summary>
        /// <param name="hospitalId"></param>
        /// <param name="second"></param>
        /// <param name="bodysources"></param>
        /// <returns></returns>
        private JArray GetBodyResultAfterAudit(int hospitalId, ag_secondallot second, List<ag_bodysource> bodysources)
        {
            JArray jArray = new JArray();
            if (bodysources == null || !bodysources.Any())
                return jArray;

            var bodyDynamic = agworkloadsourceRepository.GetEntities(t => bodysources.Select(w => w.Id).Contains(t.BodyId));

            foreach (var item in bodysources.OrderBy(t => t.Id))
            {
                JObject jObj = JObject.Parse(JsonConvert.SerializeObject(item));    //使用jsonHelper会自动隐藏空值的项

                if (bodyDynamic != null && bodyDynamic.Any(t => t.BodyId == item.Id))
                {
                    foreach (var col in bodyDynamic.Where(t => t.BodyId == item.Id))
                    {
                        jObj.Add(new JProperty(col.ItemId, col.Value));
                    }
                }

                jArray.Add(jObj);
            }
            return jArray;
        }

        /// <summary>
        /// 获取当前使用的模板
        /// </summary>
        /// <param name="hospitalId"></param>
        /// <param name="second"></param>
        /// <returns></returns>
        private (int tempId, string name) GetUsingTempId(int hospitalId, ag_secondallot second)
        {
            int usingTempId = (int)Temp.other;
            var usedTemp = agusetempRepository.GetEntity(t => t.HospitalId == hospitalId && t.Department == second.Department && t.UnitType == second.UnitType);
            if (usedTemp != null && usedTemp.UseTempId.HasValue)
            {
                usingTempId = usedTemp.UseTempId.Value;
                if (IsAudit(second.Status))
                    usingTempId = (second.UseTempId ?? 0) == 0 ? (int)Temp.other : second.UseTempId.Value;
            }
            var temp = agtempRepository.GetEntity(t => t.Id == usingTempId);
            return (usingTempId, temp?.TempName);
        }

        /// <summary>
        /// 获取上一次的二次绩效
        /// </summary>
        /// <param name="hospitalId"></param>
        /// <param name="secondAllot"></param>
        /// <returns></returns>
        private 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 prevSecondAllot = agsecondallotRepository.GetEntity(w => w.AllotId == prevAllot.ID && w.UnitType == secondAllot.UnitType && w.Department == secondAllot.Department);
            return prevSecondAllot;
        }

        /// <summary>
        /// 获取岗位
        /// </summary>
        readonly Func<per_employee, string> getPost = (e) => (e.Duty?.IndexOf("主任") > -1 || e.Duty?.IndexOf("护士长") > -1) ? "科主任/护士长" : "其他";

        /// <summary>
        /// 二次绩效分配录入人员自动补全信息
        /// </summary>
        /// <param name="secodId"></param>
        /// <param name="request">工号、姓名</param>
        /// <returns></returns>
        public JArray AutoComplete(int secodId, SecondEmployeeRequest request)
        {
            var second = agsecondallotRepository.GetEntity(t => t.Id == secodId);
            if (second == null)
                throw new PerformanceException("当前科室二次分配绩效信息无效");

            var allot = perallotRepository.GetEntity(w => w.ID == second.AllotId);
            if (allot == null)
                throw new PerformanceException("当前绩效信息无效");

            var usetemp = agusetempRepository.GetEntity(t => t.HospitalId == allot.HospitalId && t.Department == second.Department && t.UnitType == second.UnitType);
            if (usetemp == null)
                throw new PerformanceException("当前科室暂未配置绩效模板");

            #region 接口响应速度优化
            var employees = new List<per_employee>();

            if (!string.IsNullOrEmpty(request?.EmployeeName))
                employees = peremployeeRepository.GetEntities(w => w.AllotId == second.AllotId.Value && w.DoctorName == request.EmployeeName);
            if (!string.IsNullOrEmpty(request?.JobNumber))
                employees = peremployeeRepository.GetEntities(w => w.AllotId == second.AllotId.Value && w.PersonnelNumber == request.JobNumber);
            #endregion

            employees = employees?.OrderBy(t => t.PersonnelNumber).ThenByDescending(t => t.DoctorName).ToList();
            if (employees == null || !employees.Any()) return new JArray();

            var bodysources = new List<ag_bodysource>();

            if (employees.Any(t => !string.IsNullOrEmpty(t.DoctorName)))
                bodysources = GetEmployeeFromEmployeeDict(employees.Where(t => !string.IsNullOrEmpty(t.DoctorName)).ToList());

            else
                bodysources = GetEmployeeFromEmployeeDict(employees);

            SupplementOtherPerfor(second, bodysources, employees, false);
            return GetBodyResult(allot.HospitalId, second, bodysources);
        }

        #endregion

        #region 动态配置字典

        public dynamic GetWorkTypeDict(int secondId)
        {
            var worktypeSources = agworktypesourceRepository.GetEntities(t => t.SecondId == secondId) ?? new List<ag_worktype_source>();
            return worktypeSources.OrderBy(t => t.Id).Select(t => new
            {
                Title = t.FieldId,
                Value = t.FieldName
            });
        }

        public dynamic GetWorkloadDict(int secondId)
        {
            var second = agsecondallotRepository.GetEntity(t => t.Id == secondId);
            if (second == null) throw new PerformanceException("参数SecondId无效！");

            if (IsAudit(second.Status)) return GetWorkloadDictAfterAudit(secondId);

            var allot = perallotRepository.GetEntity(t => t.ID == second.AllotId);
            if (allot == null) throw new PerformanceException("绩效记录不存在！");

            var workloads = agworkloadRepository.GetEntities(t => t.HospitalId == allot.HospitalId && t.Department == second.Department && t.UnitType == second.UnitType)
                ?? new List<ag_workload>();
            return workloads.OrderBy(t => t.WorkTypeId).ThenBy(t => t.Sort).Select(t => new
            {
                Title = t.ItemId,
                Value = t.ItemName,
                Factor = t.FactorValue
            });
        }

        private dynamic GetWorkloadDictAfterAudit(int secondId)
        {
            var bodysources = agbodysourceRepository.GetEntities(t => t.SecondId == secondId);
            if (bodysources == null || !bodysources.Any()) return new string[] { };

            var workloadsources = agworkloadsourceRepository.GetEntities(t => bodysources.Select(w => w.Id).Contains(t.BodyId));
            if (workloadsources == null || !workloadsources.Any()) return new string[] { };

            var dict = workloadsources.GroupBy(t => new { t.ItemId, t.ItemName, t.FactorValue, t.Sort, t.WorkTypeId, t.WorkloadId })
                .OrderBy(t => t.Key.WorkTypeId).ThenBy(t => t.Key.Sort)
                .Select(t => new
                {
                    Title = t.Key.ItemId,
                    Value = t.Key.ItemName,
                    Factor = t.Key.FactorValue
                });

            return dict;
        }

        #endregion

        #region 保存数据
        public void SaveSecondAllotData(int secondId, dynamic saveData)
        {
            try
            {
                var second = agsecondallotRepository.GetEntity(t => t.Id == secondId);
                if (second == null) throw new PerformanceException("参数SecondId无效！");

                var allot = perallotRepository.GetEntity(t => t.ID == second.AllotId);
                if (allot == null) throw new PerformanceException("绩效记录不存在！");

                var head = saveData["head"];
                SaveSecondAllotHeadData(secondId, JsonHelper.Serialize(head));

                var body = saveData["body"];
                SaveSecondAllotBodyData(allot.HospitalId, second, body);
            }
            catch (Exception ex)
            {
                logger.LogError(ex.Message);
            }
        }

        /// <summary>
        /// 保存Head相关数据
        /// </summary>
        /// <param name="secondId"></param>
        /// <param name="json"></param>
        private void SaveSecondAllotHeadData(int secondId, string json)
        {
            if (string.IsNullOrEmpty(json)) return;

            ag_headsource headsource = JsonHelper.Deserialize<ag_headsource>(json);
            if (headsource == null) return;

            headsource.SecondId = secondId;
            if (headsource.Id == 0)
            {
                agheadsourceRepository.Add(headsource);
            }
            else
            {
                agheadsourceRepository.UpdateByState(headsource);
            }

            string[] prefix = new string[] { "Workload_Ratio_", "Workload_Amount_" };
            Dictionary<string, object> dict = JsonHelper.Deserialize<Dictionary<string, object>>(json);
            var keys = dict.Keys.Where(t => t.StartsWith(prefix[0]) || t.StartsWith(prefix[1]));
            if (keys == null || !keys.Any())
                return;

            List<ag_worktype_source> insertData = new List<ag_worktype_source>();
            var worktypeSources = agworktypesourceRepository.GetEntities(t => t.SecondId == secondId);
            if (worktypeSources == null || !worktypeSources.Any())
                return;

            foreach (var key in keys)
            {
                var update = worktypeSources.FirstOrDefault(t => t.FieldId == key);
                if (update != null)
                {
                    update.Value = ConvertHelper.To<decimal>(dict[key]);
                }
            }
            agworktypesourceRepository.UpdateRange(worktypeSources.ToArray());
        }

        /// <summary>
        /// 保存Body相关数据
        /// </summary>
        /// <param name="hospitalId"></param>
        /// <param name="second"></param>
        /// <param name="body"></param>
        private void SaveSecondAllotBodyData(int hospitalId, ag_secondallot second, dynamic body)
        {
            // 允许空行数据提交，删除数据库存数数据
            var bodyEntities = agbodysourceRepository.GetEntities(t => t.SecondId == second.Id);
            if (bodyEntities != null && bodyEntities.Any())
            {
                var workloadEntities = agworkloadsourceRepository.GetEntities(t => bodyEntities.Select(w => w.Id).Contains(t.BodyId));
                if (workloadEntities != null && workloadEntities.Any())
                    agworkloadsourceRepository.RemoveRange(workloadEntities.ToArray());

                agbodysourceRepository.RemoveRange(bodyEntities.ToArray());
            }

            if (body == null || !((IEnumerable<dynamic>)body).Any()) return;

            var workloads = agworkloadRepository.GetEntities(t => t.HospitalId == hospitalId && t.Department == second.Department && t.UnitType == second.UnitType)?.ToList();
            if (workloads == null || !workloads.Any()) return;

            string[] prefix = new string[] { "WorkloadScore_", "AssessmentScore_", "WorkPerformance_", $"{AgWorkloadType.SingleAwards}_", $"{AgWorkloadType.Workload}_" };
            List<ag_workload_source> workloadSources = new List<ag_workload_source>();
            foreach (var rowitem in body)
            {
                ag_bodysource bodySource = JsonHelper.Deserialize<ag_bodysource>(JsonHelper.Serialize(rowitem));
                bodySource.SecondId = second.Id;
                var result = agbodysourceRepository.Add(bodySource);

                if (!result) continue;

                Dictionary<string, object> dict = JsonHelper.Deserialize<Dictionary<string, object>>(JsonHelper.Serialize(rowitem));
                var keys = dict.Keys.Where(t => t.StartsWith(prefix[0]) || t.StartsWith(prefix[1]) || t.StartsWith(prefix[2]) || t.StartsWith(prefix[3]) || t.StartsWith(prefix[4]));
                if (keys == null || !keys.Any()) continue;

                foreach (var key in keys)
                {
                    var workload = workloads.FirstOrDefault(t => t.ItemId == key);
                    if (workload == null) continue;

                    workloadSources.Add(new ag_workload_source
                    {
                        WorkloadId = workload.Id,
                        BodyId = bodySource.Id,
                        ItemId = workload.ItemId,
                        ItemName = workload.ItemName,
                        FactorValue = workload.FactorValue,
                        Sort = workload.Sort,
                        Value = ConvertHelper.To<decimal>(dict[key]),
                        WorkTypeId = workload.WorkTypeId
                    });
                }
            }
            if (workloadSources != null && workloadSources.Any())
                agworkloadsourceRepository.AddRange(workloadSources.ToArray());
        }

        #endregion
    }
}
