using AutoMapper;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
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.Linq.Expressions;
using System.Text;

namespace Performance.Services
{
    public class PersonService : IAutoInjection
    {
        private readonly ILogger<PersonService> logger;
        private readonly PerforPerdeptdicRepository perdeptdicRepository;
        private readonly PerforPeremployeeRepository peremployeeRepository;
        private readonly PerforPerallotRepository perallotRepository;
        private readonly PerforUserRepository perforUserRepository;
        private readonly PerforUserroleRepository perforUserroleRepository;
        private readonly PerforRoleRepository perforRoleRepository;
        private readonly PerforAgsecondallotRepository agsecondallotRepository;
        private readonly Application application;
        private readonly Dictionary<string, (string, string)> dict = new Dictionary<string, (string, string)>
        {
            { nameof(DeptdicResponse.OutDoctorAccounting), (UnitType.医生组.ToString(), "门诊") },
            { nameof(DeptdicResponse.OutNurseAccounting), (UnitType.护理组.ToString(), "门诊") },
            { nameof(DeptdicResponse.OutTechnicAccounting), (UnitType.医技组.ToString(), "门诊") },
            { nameof(DeptdicResponse.InpatDoctorAccounting), (UnitType.医生组.ToString(), "住院") },
            { nameof(DeptdicResponse.InpatNurseAccounting), (UnitType.护理组.ToString(), "住院") },
            { nameof(DeptdicResponse.InpatTechnicAccounting), (UnitType.医技组.ToString(), "住院") },
            { nameof(DeptdicResponse.LogisticsAccounting), (UnitType.行政后勤.ToString(), null) },
            { nameof(DeptdicResponse.SpecialAccounting), (UnitType.特殊核算组.ToString(), null) },
        };

        public PersonService(ILogger<PersonService> logger,
            PerforPerdeptdicRepository perdeptdicRepository,
            PerforPeremployeeRepository peremployeeRepository,
            PerforPerallotRepository perallotRepository,
            PerforUserRepository perforUserRepository,
            PerforUserroleRepository perforUserroleRepository,
            PerforRoleRepository perforRoleRepository,
            PerforAgsecondallotRepository agsecondallotRepository,
            IOptions<Application> application)
        {
            this.logger = logger;
            this.perdeptdicRepository = perdeptdicRepository;
            this.peremployeeRepository = peremployeeRepository;
            this.perallotRepository = perallotRepository;
            this.perforUserRepository = perforUserRepository;
            this.perforUserroleRepository = perforUserroleRepository;
            this.perforRoleRepository = perforRoleRepository;
            this.agsecondallotRepository = agsecondallotRepository;
            this.application = application.Value;
        }

        /// <summary>
        /// 创建绩效时，批量创建员工信息
        /// </summary>
        /// <param name="hospitalId"></param>
        /// <param name="allotId"></param>
        /// <param name="prevAllotId"></param>
        public void CreateAllotPersons(int hospitalId, int allotId, int prevAllotId = -1)
        {
            var allot = perallotRepository.GetEntity(t => t.ID == allotId);
            if (allot == null) throw new PerformanceException("绩效信息错误！");

            var isExist = (peremployeeRepository.GetEntities(t => t.HospitalId == hospitalId && t.AllotId == allotId)?.Count ?? 0) > 0;
            if (isExist) return;

            List<per_employee> persons = new List<per_employee>();
            if (new int[] { -1, 0 }.Contains(prevAllotId))
            {
                persons = peremployeeRepository.GetEntities(t => t.HospitalId == hospitalId && t.AllotId == prevAllotId);
                if (persons == null || !persons.Any())
                    persons = peremployeeRepository.GetEntities(t => t.HospitalId == hospitalId && t.AllotId == -1);
            }
            else
                persons = peremployeeRepository.GetEntities(t => t.HospitalId == hospitalId && t.AllotId == -1);

            if (persons == null || !persons.Any()) return;

            int day = DateTime.DaysInMonth(allot.Year, allot.Month);

            var data = persons.Select(t => new per_employee
            {
                AccountingUnit = t.AccountingUnit,
                Department = t.Department,
                DoctorName = t.DoctorName,
                PersonnelNumber = t.PersonnelNumber,
                JobCategory = t.JobCategory,
                Duty = t.Duty,
                JobTitle = t.JobTitle,
                UnitType = t.UnitType,
                AttendanceDay = day,
                Attendance = 1,
                PermanentStaff = t.PermanentStaff,
                EfficiencyNumber = t.EfficiencyNumber,
                WorkTime = t.WorkTime,
                BirthDate = t.BirthDate,
                Age = t.Age,
                Remark = t.Remark,
                HospitalId = t.HospitalId,
                AllotId = allotId,
                ReservedRatio = t.ReservedRatio,
                CreateTime = DateTime.Now,
            }).ToList();
            SaveAllotPersons(data);
        }

        private void SaveAllotPersons(List<per_employee> employees)
        {
            try
            {
                if (employees.Count > 1000)
                {
                    int rows = 500;
                    for (int i = 0; i < Math.Ceiling((double)employees.Count / rows); i++)
                    {
                        peremployeeRepository.AddRange(employees.Skip(rows * i).Take(rows).ToArray());
                    }
                }
                else
                    peremployeeRepository.AddRange(employees.ToArray());
            }
            catch (Exception ex)
            {
                logger.LogError(ex.ToString());
            }
        }

        /// <summary>
        /// 获取所有员工记录
        /// </summary>
        /// <param name="allotId"></param>
        /// <returns></returns>
        public List<per_employee> GetPerEmployee(int allotId)
        {
            return peremployeeRepository.GetEntities(t => t.AllotId == allotId)?.OrderBy(t => t.Id).ToList();
        }

        /// <summary>
        /// 获取所有员工记录
        /// </summary>
        /// <param name="allotId"></param>
        /// <returns></returns>
        public List<per_employee> GetPersons(int allotId, int userId)
        {
            var (dept, unittype) = GetDeptByUser(userId);
            Expression<Func<per_employee, bool>> exp = t => t.AllotId == allotId;
            if (!string.IsNullOrEmpty(dept) && unittype.Any())
            {
                exp = exp.And(t => t.AccountingUnit == dept && unittype.Contains(t.UnitType));
            }

            var list = peremployeeRepository.GetEntities(exp);
            if (list != null && list.Any())
                return list.OrderBy(t => t.Id).ToList();

            return new List<per_employee>();
        }

        /// <summary>
        /// 新增员工信息
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public per_employee CreatePerson(PerEmployeeResponse request)
        {
            var employee = peremployeeRepository.GetEntity(t => t.AllotId == request.AllotId && t.PersonnelNumber == request.PersonnelNumber
            && t.DoctorName == request.DoctorName && t.Department == request.Department);
            if (employee != null)
                throw new PerformanceException($"员工工号为{request.PersonnelNumber}的数据已存在！");

            var unittype = EnumHelper.GetItems<UnitType>().Select(t => t.Description).ToList();
            unittype.AddRange(EnumHelper.GetItems<AccountUnitType>().Select(t => t.Description));

            if (!unittype.Contains(request.UnitType))
                throw new PerformanceException($"人员类别不符合规范！");

            var entity = Mapper.Map<per_employee>(request);
            var allot = perallotRepository.GetEntity(t => t.ID == request.AllotId);
            int day = DateTime.DaysInMonth(allot.Year, allot.Month);
            entity.Attendance = request.AttendanceDay / day;
            entity.CreateTime = DateTime.Now;

            //CheckAccountingDept(request.HospitalId.Value, request.AccountingUnit, request.Department);
            peremployeeRepository.Add(entity);
            return entity;
        }

        /// <summary>
        /// 修改员工信息
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public bool UpdatePerson(PerEmployeeResponse request)
        {
            var employee = peremployeeRepository.GetEntity(t => t.AllotId == request.AllotId && t.PersonnelNumber == request.PersonnelNumber
            && t.DoctorName == request.DoctorName && t.Department == request.Department);
            if (employee != null && employee.Id != request.Id)
                throw new PerformanceException($"员工工号为{request.PersonnelNumber}的数据已存在！");

            var unittype = EnumHelper.GetItems<UnitType>().Select(t => t.Description).ToList();
            unittype.AddRange(EnumHelper.GetItems<AccountUnitType>().Select(t => t.Description));

            if (!unittype.Contains(request.UnitType))
                throw new PerformanceException($"人员类别不符合规范！");

            if (employee == null)
                employee = peremployeeRepository.GetEntity(t => t.Id == request.Id) ?? throw new PerformanceException("人员信息无效！");

            //Mapper.Map(request, employee, typeof(per_employee), typeof(per_employee));

            employee.AccountingUnit = request.AccountingUnit;
            employee.Department = request.Department;
            employee.DoctorName = request.DoctorName;
            employee.PersonnelNumber = request.PersonnelNumber;
            employee.JobCategory = request.JobCategory;
            employee.Duty = request.Duty;
            employee.JobTitle = request.JobTitle;
            employee.UnitType = request.UnitType;
            employee.WorkTime = request.WorkTime == null ? null : ConvertHelper.To<DateTime?>(request.WorkTime);
            employee.BirthDate = request.BirthDate == null ? null : ConvertHelper.To<DateTime?>(request.BirthDate);
            employee.Age = request.Age;
            employee.Remark = request.Remark;
            employee.EfficiencyNumber = request.EfficiencyNumber;
            employee.PermanentStaff = request.PermanentStaff;
            employee.AttendanceDay = request.AttendanceDay;

            // 出勤率
            var allot = perallotRepository.GetEntity(t => t.ID == request.AllotId);
            var day = (decimal)DateTime.DaysInMonth(allot.Year, allot.Month);
            employee.Attendance = Math.Round((request.AttendanceDay ?? 0) / day, 4);

            //CheckAccountingDept(request.HospitalId.Value, request.AccountingUnit, request.Department);
            return peremployeeRepository.Update(employee);
        }

        /// <summary>
        /// 删除员工
        /// </summary>
        /// <param name="employeeId"></param>
        /// <returns></returns>
        public bool DeletePerson(int employeeId)
        {
            var employee = peremployeeRepository.GetEntity(t => t.Id == employeeId);
            if (employee == null)
                throw new PerformanceException($"员工信息错误！");

            return peremployeeRepository.Remove(employee);
        }

        /// <summary>
        /// 检查核算单元是否已存在
        /// </summary>
        /// <param name="hosapitalId">医院Id</param>
        /// <param name="accountingUnit">核算单元</param>
        /// <param name="department">科室</param>
        private void CheckAccountingDept(int hosapitalId, string accountingUnit, string department)
        {
            var accountDept = perdeptdicRepository.GetEntity(t => t.HospitalId == hosapitalId && t.AccountingUnit == accountingUnit && t.Department == department);
            if (accountDept == null)
            {
                var entity = new per_dept_dic
                {
                    AccountingUnit = accountingUnit,
                    Department = department,
                    HospitalId = hosapitalId,
                    Source = "人事科增加人员记录时补充"
                };
                perdeptdicRepository.Add(entity);
            }
        }

        /// <summary>
        /// 获取给医院所有的
        /// </summary>
        /// <param name="hospitalId"></param>
        /// <returns></returns>
        public IEnumerable<DeptdicResponse> GetDepartments(int hospitalId)
        {
            var depts = perdeptdicRepository.GetEntities(t => t.HospitalId == hospitalId);
            if (depts == null || !depts.Any()) return null;

            var result = depts.GroupBy(t => new { t.HISDeptName, t.Department }).Select(t => new DeptdicResponse
            {
                HospitalId = hospitalId,
                HISDeptName = t.Key.HISDeptName,
                Department = t.Key.Department,
                OutDoctorAccounting = GetDeptdic(t.FirstOrDefault(group => group.Department == t.Key.Department && group.HISDeptName == t.Key.HISDeptName && group.UnitType == UnitType.医生组.ToString() && group.Source == "门诊")),
                OutNurseAccounting = GetDeptdic(t.FirstOrDefault(group => group.Department == t.Key.Department && group.HISDeptName == t.Key.HISDeptName && group.UnitType == UnitType.护理组.ToString() && group.Source == "门诊")),
                OutTechnicAccounting = GetDeptdic(t.FirstOrDefault(group => group.Department == t.Key.Department && group.HISDeptName == t.Key.HISDeptName && group.UnitType == UnitType.医技组.ToString() && group.Source == "门诊")),
                InpatDoctorAccounting = GetDeptdic(t.FirstOrDefault(group => group.Department == t.Key.Department && group.HISDeptName == t.Key.HISDeptName && group.UnitType == UnitType.医生组.ToString() && group.Source == "住院")),
                InpatNurseAccounting = GetDeptdic(t.FirstOrDefault(group => group.Department == t.Key.Department && group.HISDeptName == t.Key.HISDeptName && group.UnitType == UnitType.护理组.ToString() && group.Source == "住院")),
                InpatTechnicAccounting = GetDeptdic(t.FirstOrDefault(group => group.Department == t.Key.Department && group.HISDeptName == t.Key.HISDeptName && group.UnitType == UnitType.医技组.ToString() && group.Source == "住院")),
                LogisticsAccounting = GetDeptdic(t.FirstOrDefault(group => group.Department == t.Key.Department && group.HISDeptName == t.Key.HISDeptName && group.UnitType == UnitType.行政后勤.ToString())),
                SpecialAccounting = GetDeptdic(t.FirstOrDefault(group => group.Department == t.Key.Department && group.HISDeptName == t.Key.HISDeptName && group.UnitType == UnitType.特殊核算组.ToString())),
                CreateTime = t.Max(group => group.CreateTime)
            });

            return result.OrderByDescending(t => t.CreateTime).ThenBy(t => t.Department);
        }

        private (string dept, string[] unittype) GetDeptByUser(int userId)
        {
            var user = perforUserRepository.GetEntity(t => t.ID == userId && t.IsDelete == 1);
            if (user == null) throw new PerformanceException("用户信息错误");

            var userrole = perforUserroleRepository.GetEntity(t => t.UserID == user.ID);
            var role = perforRoleRepository.GetEntity(t => t.ID == userrole.RoleID);

            if (role == null) return ("", new string[] { });

            Dictionary<int, string[]> dict = new Dictionary<int, string[]>
            {
                {  application.DirectorRole, new string[]{ UnitType.医生组.ToString(), UnitType.其他医生组.ToString(), UnitType.医技组.ToString(), UnitType.其他医技组.ToString() } },
                {  application.NurseRole, new string[]{ UnitType.护理组.ToString() } },
                {  application.SpecialRole, new string[]{ UnitType.特殊核算组.ToString() } },
                {  application.OfficeRole, new string[]{ UnitType.行政后勤.ToString() } },
            };

            if (dict.Keys.Contains(role.Type.Value))
            {
                return (user.Department, dict[role.Type.Value]);
            }

            return ("", new string[] { });
        }

        private Deptdic GetDeptdic(per_dept_dic dic)
        {
            if (dic == null) return new Deptdic();

            return new Deptdic
            {
                Id = dic.Id,
                AccountingUnit = dic.AccountingUnit
            };
        }

        /// <summary>
        /// 创建科室核算信息
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public per_dept_dic CreateDeptDic(per_dept_dic request)
        {
            var deptdic = perdeptdicRepository.GetEntity(t => t.HospitalId == request.HospitalId && t.AccountingUnit == request.AccountingUnit
            && t.Department == request.Department && t.HISDeptName == request.HISDeptName && t.Source == request.Source && t.UnitType == request.UnitType);

            if (deptdic != null)
                throw new PerformanceException($"{request.Department}数据重复！");

            request.CreateTime = DateTime.Now;
            perdeptdicRepository.Add(request);

            return request;
        }

        /// <summary>
        /// 更新科室核算信息
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public bool UpdateDeptDic(per_dept_dic request)
        {
            var deptdic = perdeptdicRepository.GetEntity(t => t.HospitalId == request.HospitalId && t.AccountingUnit == request.AccountingUnit
            && t.Department == request.Department && t.Source == request.Source && t.UnitType == request.UnitType);

            if (deptdic != null && deptdic.Id != request.Id)
                throw new PerformanceException($"{request.Department}数据重复！");

            Mapper.Map(request, deptdic, typeof(per_dept_dic), typeof(per_dept_dic));
            return perdeptdicRepository.Add(deptdic);
        }

        /// <summary>
        /// 更新科室核算信息
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public bool UpdateDeptDic(DeptdicResponse request)
        {
            try
            {
                foreach (var item in dict)
                {
                    var property = typeof(DeptdicResponse).GetProperty(item.Key);
                    if (property == null) continue;

                    var deptdic = property.GetValue(request);
                    if (deptdic is Deptdic)
                    {
                        var data = (Deptdic)deptdic;
                        if (string.IsNullOrEmpty(data.AccountingUnit) && data.Id == 0)
                            continue;

                        if (data.Id > 0)
                        {
                            var entity = perdeptdicRepository.GetEntity(t => t.Id == data.Id);
                            if (entity == null) continue;
                            entity.HISDeptName = request.HISDeptName;
                            entity.Department = request.Department;
                            entity.AccountingUnit = data.AccountingUnit;
                            perdeptdicRepository.Update(entity);
                        }
                        else
                        {
                            var entity = new per_dept_dic
                            {
                                HospitalId = request.HospitalId,
                                HISDeptName = request.HISDeptName,
                                Department = request.Department,
                                AccountingUnit = data.AccountingUnit,
                                UnitType = item.Value.Item1,
                                Source = item.Value.Item2,
                                CreateTime = DateTime.Now,
                            };
                            perdeptdicRepository.Add(entity);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                logger.LogError(ex.ToString());
                return false;
            }

            return true;
        }

        /// <summary>
        /// 删除科室核算信息
        /// </summary>
        /// <param name="deptdicId"></param>
        /// <returns></returns>
        public bool DeleteDeptDic(DeptdicResponse request)
        {
            var deptdics = perdeptdicRepository.GetEntities(t => (t.HISDeptName ?? "") == request.HISDeptName
            && (t.Department ?? "") == request.Department && t.HospitalId == request.HospitalId);
            if (deptdics == null || !deptdics.Any())
                throw new PerformanceException("科室记录不存在!");

            return perdeptdicRepository.RemoveRange(deptdics.ToArray());
        }

        /// <summary>
        /// 系统/标准科室字典
        /// </summary>
        /// <param name="hospitalId">医院Id</param>
        /// <param name="type">1系统科室 2标准科室 3核算单元</param>
        /// <returns></returns>
        public List<TitleValue> DeptDics(int hospitalId, int type)
        {
            var deptdics = perdeptdicRepository.GetEntities(t => t.HospitalId == hospitalId);
            if (deptdics == null || !deptdics.Any()) return new List<TitleValue>();

            var result = new List<string>();
            if (type == 1)
            {
                result = deptdics.Select(t => t.HISDeptName).Distinct().ToList();
            }
            else if (type == 2)
            {
                result = deptdics.Select(t => t.Department).Distinct().ToList();
            }
            else if (type == 3)
            {
                result = deptdics.Where(t => !new string[] { UnitType.行政后勤.ToString(), UnitType.特殊核算组.ToString() }.Contains(t.UnitType)).Select(t => t.AccountingUnit).Distinct().ToList();
            }
            else if (type == 4)
            {
                result = deptdics.Where(t => t.UnitType == UnitType.行政后勤.ToString()).Select(t => t.AccountingUnit).Distinct().ToList();
            }
            else if (type == 5)
            {
                result = deptdics.Where(t => t.UnitType == UnitType.特殊核算组.ToString()).Select(t => t.AccountingUnit).Distinct().ToList();
            }
            return result.Select(t => new TitleValue { Title = t, Value = t }).ToList();
        }

        /// <summary>
        /// 科室工作量数据详情
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public object DeptWorkloadDetail(WorkDetailRequest request, int userId)
        {
            var second = agsecondallotRepository.GetEntity(w => w.Id == request.SecondId);
            if (second == null)
                return null;

            var allot = perallotRepository.GetEntity(w => w.ID == request.AllotId);
            if (allot == null)
                return null;

            var data = perallotRepository.QueryWorkloadData(request.AllotId, request.AccountingUnit, second.UnitType, allot.HospitalId);
            if (data != null && data.Any())
            {
                return data.GroupBy(t => new { t.Department, t.DoctorName, t.PersonnelNumber, t.Category })
                    .Select(t => new DeptIncomeResponse
                    {
                        Department = t.Key.Department,
                        DoctorName = t.Key.DoctorName,
                        PersonnelNumber = t.Key.PersonnelNumber,
                        Category = t.Key.Category,
                        Fee = t.Sum(group => group.Fee ?? 0)
                    }).OrderBy(t => t.PersonnelNumber).ThenBy(t => t.Category);
            }

            return new string[] { };
        }

        /// <summary>
        /// 科室工作量数据详情
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public object DeptIncomeDetail(WorkDetailRequest request, int userId)
        {
            var second = agsecondallotRepository.GetEntity(w => w.Id == request.SecondId);
            if (second == null)
                return null;

            var allot = perallotRepository.GetEntity(w => w.ID == request.AllotId);
            if (allot == null)
                return null;

            var data = perallotRepository.QueryIncomeData(request.AllotId, request.AccountingUnit, second.UnitType, allot.HospitalId);
            if (data != null && data.Any())
            {
                return data.GroupBy(t => new { t.Department, t.DoctorName, t.PersonnelNumber, t.Category })
                    .Select(t => new DeptIncomeResponse
                    {
                        Department = t.Key.Department,
                        DoctorName = t.Key.DoctorName,
                        PersonnelNumber = t.Key.PersonnelNumber,
                        Category = t.Key.Category,
                        Fee = t.Sum(group => group.Fee ?? 0)
                    }).OrderBy(t => t.PersonnelNumber).ThenBy(t => t.Category);
            }

            return new string[] { };
        }
    }
}
