using AutoMapper;
using Microsoft.Extensions.Options;
using Performance.DtoModels;
using Performance.DtoModels.AppSettings;
using Performance.EntityModels;
using Performance.Repository;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Performance.Services
{
    public class AgainAllotService : IAutoInjection
    {
        private Application application;
        private AgainService againService;
        private RoleService roleService;
        private ConfigService configService;
        private PerforCofagainRepository perforCofagainRepository;
        private PerforPeragainallotRepository perforPeragainallotRepository;
        private PerforResaccountRepository perforResaccountRepository;
        //private PerforResaccountnurseRepository perforResaccountnurseRepository;
        private PerforUserRepository perforUserRepository;
        private PerforUserhospitalRepository perforUserhospitalRepository;
        private PerforPerallotRepository perforPerallotRepository;

        private PerforAgagainsituationRepository perforAgagainsituationRepository;
        private PerforAgdataRepository perforAgdataRepository;
        private PerforAgemployeeRepository perforAgemployeeRepository;
        private PerforAgheaderRepository perforAgheaderRepository;

        public AgainAllotService(IOptions<Application> options, AgainService againService,
            RoleService roleService,
            PerforCofagainRepository perforCofagainRepository,
            PerforPeragainallotRepository perforPeragainallotRepository,
            PerforResaccountRepository perforResaccountRepository,
            //PerforResaccountnurseRepository perforResaccountnurseRepository,
            PerforUserRepository perforUserRepository,
            PerforUserhospitalRepository perforUserhospitalRepository,
            PerforPerallotRepository perforPerallotRepository,
            PerforAgagainsituationRepository perforAgagainsituationRepository,
            PerforAgdataRepository perforAgdataRepository,
            PerforAgemployeeRepository perforAgemployeeRepository,
            PerforAgheaderRepository perforAgheaderRepository,
            ConfigService configService)
        {
            this.application = options.Value;
            this.againService = againService;
            this.roleService = roleService;
            this.perforCofagainRepository = perforCofagainRepository;
            this.perforPeragainallotRepository = perforPeragainallotRepository;
            this.perforResaccountRepository = perforResaccountRepository;
            //this.perforResaccountnurseRepository = perforResaccountnurseRepository;
            this.perforUserRepository = perforUserRepository;
            this.perforUserhospitalRepository = perforUserhospitalRepository;
            this.perforPerallotRepository = perforPerallotRepository;

            this.perforAgagainsituationRepository = perforAgagainsituationRepository;
            this.perforAgdataRepository = perforAgdataRepository;
            this.perforAgemployeeRepository = perforAgemployeeRepository;
            this.perforAgheaderRepository = perforAgheaderRepository;

            this.configService = configService;
        }

        /// <summary>
        /// 生成二次绩效
        /// </summary>
        /// <param name="request"></param>
        public bool Generate(AgainAllotRequest request, int userId, string department)
        {
            var againAllot = perforPeragainallotRepository.GetEntity(t => t.ID == request.AgainAllotID);
            if (againAllot == null || againAllot.ID == 0)
                throw new PerformanceException("绩效二次分配不存在");

            var roles = roleService.GetRole(userId);

            perforPeragainallotRepository.Update(againAllot, p => { p.States = 2; });

            //清理二次绩效无用数据
            configService.ClearAgain(againAllot.ID);
            try
            {
                #region 基础信息
                //获取基础配置信息
                var config = perforCofagainRepository.GetEntities(t => t.AllotID == againAllot.AllotID);
                var jobfactor = config.FirstOrDefault(t => t.Type == 1)?.Value;
                var workfactor = config.FirstOrDefault(t => t.Type == 2)?.Value;
                var days = config.FirstOrDefault(t => t.Type == 3)?.Value;

                decimal? basicnumber = 0m;
                //获取科室实发绩效
                if (roles != null)
                {
                    var role = roles.FirstOrDefault();
                    if (role != null)
                    {
                        if (application.NurseRole == role.Type)
                            basicnumber = perforResaccountRepository.GetEntity(t => t.UnitType == (int)UnitType.护理组 && t.AllotID == againAllot.AllotID && t.AccountingUnit == department)?.RealGiveFee;
                        else if (application.DirectorRole == role.Type)
                            basicnumber = perforResaccountRepository.GetEntity(t => t.UnitType != (int)UnitType.护理组 && t.AllotID == againAllot.AllotID && t.AccountingUnit == department)?.RealGiveFee;
                    }
                }
                #endregion

                #region 计算
                //读取二次计算excel数据
                var perAgainExcel = againService.ReadData(againAllot);
                //护士长或科主任出勤
                var bossAttendance = perAgainExcel.AgainEmployee.FirstOrDefault(t => t.JobTitle == "护士长")?.Attendance;
                //计算职称出勤系数、年资出勤系数
                foreach (var item in perAgainExcel.AgainEmployee)
                {
                    item.JobAttendanceFactor = item.JobFactor * item.Attendance / days;
                    item.YearAttendanceFactor = item.Attendance * item.YearFactor / days;
                }
                //计算 科室系数人均
                var departmentFactorAvg = basicnumber / perAgainExcel.AgainEmployee.Sum(t => t.YearAttendanceFactor);
                //二次分配科室概览
                PerAgainSituation situation = new PerAgainSituation
                {
                    NightShift = perAgainExcel.AgainEmployee.Sum(t => t.NightShift),
                    DepartmentTotal = basicnumber,
                    BossPerfor = departmentFactorAvg * bossAttendance / days,
                    Award = perAgainExcel.AgainEmployee.Sum(t => t.Award),
                    Allowance = perAgainExcel.AgainEmployee.Sum(t => t.Allowance),
                    AlonePerfor = perAgainExcel.AgainEmployee.Sum(t => t.AlonePerfor),
                    Attendance = days,
                    DepartmentFactorAvg = departmentFactorAvg
                };
                //业绩二次分配科室概览：业绩分配绩效、职称绩效、工作量绩效
                situation.AllotPerfor = situation.DepartmentTotal - situation.BossPerfor - situation.Award - situation.Allowance - situation.AlonePerfor;
                situation.JobPerfor = situation.AllotPerfor * jobfactor;
                situation.WorkloadPerfor = situation.AllotPerfor * workfactor;

                var rowList = perAgainExcel.AgainData.Select(t => t.RowNumber).Distinct().ToList();
                //业绩二次分配科室动态数据：工作量得分
                foreach (var rowNumber in rowList)
                {
                    var againEmployee = perAgainExcel.AgainEmployee.FirstOrDefault(t => t.RowNumber == rowNumber);
                    var atRowList = perAgainExcel.AgainData.Where(t => t.RowNumber == rowNumber);
                    decimal? sumValue = 0m;
                    foreach (var atRow in atRowList)
                        sumValue += atRow.IsFactor == 1 ? atRow.IsFactor * atRow.CellValue : atRow.CellValue;
                    sumValue = sumValue * againEmployee?.YearFactor;

                    var head = perAgainExcel.Header.Children.FirstOrDefault(t => t.CellValue == "工作量得分");
                    if (head == null)
                    {
                        perAgainExcel.Header.MergeCell++;
                        var pointrow = perAgainExcel.Header.Children.Max(t => t.PointRow);
                        var pointcell = perAgainExcel.Header.Children.Max(t => t.PointCell);
                        head = new PerHeader(pointrow, pointcell, "工作量得分", 1, 1, 1, null, 1);
                        perAgainExcel.Header.Children.Add(head);
                    }
                    perAgainExcel.AgainData.Add(new PerAgainData(rowNumber.Value, "工作量得分", sumValue, 1, 2, null, "", "", head.SignID));
                }
                //业绩二次分配科室动态数据：绩效工资
                foreach (var rowNumber in rowList)
                {
                    var workvalue = perAgainExcel.AgainData.FirstOrDefault(t => t.RowNumber == rowNumber && t.TypeName == "工作量得分")?.CellValue;
                    var sumvalue = perAgainExcel.AgainData.Where(t => t.TypeName == "工作量得分").Sum(t => t.CellValue);

                    var perforValue = workvalue / sumvalue * situation.WorkloadPerfor;

                    var head = perAgainExcel.Header.Children.FirstOrDefault(t => t.CellValue == "绩效工资");
                    if (head == null)
                    {
                        perAgainExcel.Header.MergeCell++;
                        var pointrow = perAgainExcel.Header.Children.Max(t => t.PointRow);
                        var pointcell = perAgainExcel.Header.Children.Max(t => t.PointCell);
                        head = new PerHeader(pointrow, pointcell, "绩效工资", 1, 1, 1, null, 1);
                        perAgainExcel.Header.Children.Add(head);
                    }
                    perAgainExcel.AgainData.Add(new PerAgainData(rowNumber.Value, "绩效工资", perforValue, 1, 2, null, "", "", head.SignID));
                }

                //业绩二次分配科室：应发绩效、实发绩效
                string[] jobArray = new string[] { "护士长", "科主任" };
                foreach (var employee in perAgainExcel.AgainEmployee)
                {
                    if (jobArray.Contains(employee.JobTitle))
                    {
                        employee.GiveFee = situation.BossPerfor;
                        employee.RealGiveFee = situation.BossPerfor;
                    }
                    else
                    {
                        employee.JobAttendancePerfor = situation.JobPerfor * employee.JobAttendanceFactor / perAgainExcel.AgainEmployee.Sum(t => t.JobAttendanceFactor);
                        var value = perAgainExcel.AgainData.FirstOrDefault(t => t.RowNumber == employee.RowNumber && t.TypeName == "绩效工资")?.CellValue;
                        employee.GiveFee = (employee.JobAttendancePerfor ?? 0) + (value ?? 0) + (employee.Award ?? 0) + (employee.Allowance ?? 0) + (employee.AlonePerfor ?? 0);
                        employee.RealGiveFee = (employee.GiveFee ?? 0) + (employee.NightShift ?? 0);
                    }
                }
                #endregion

                #region 保存

                var againsituation = Mapper.Map<ag_againsituation>(situation);
                againsituation.AllotID = againAllot.AllotID;
                againsituation.AgainAllotID = againAllot.ID;
                perforAgagainsituationRepository.Add(againsituation);

                var employeeList = Mapper.Map<List<ag_employee>>(perAgainExcel.AgainEmployee);
                employeeList.ForEach(item => { item.AllotID = againAllot.AllotID; item.AgainAllotID = againAllot.ID; });
                perforAgemployeeRepository.AddRange(employeeList.ToArray());

                var pHeader = Mapper.Map<ag_header>(perAgainExcel.Header);
                pHeader.AllotID = againAllot.AllotID;
                pHeader.AgainAllotID = againAllot.ID;
                var cHeaderList = Mapper.Map<List<ag_header>>(perAgainExcel.Header.Children);
                cHeaderList.ForEach(item => { item.AllotID = againAllot.AllotID; item.AgainAllotID = againAllot.ID; });
                perforAgheaderRepository.Add(pHeader);
                perforAgheaderRepository.AddRange(cHeaderList.ToArray());

                var dataList = Mapper.Map<List<ag_data>>(perAgainExcel.AgainData);
                dataList.ForEach(item => { item.AllotID = againAllot.AllotID; item.AgainAllotID = againAllot.ID; });
                perforAgdataRepository.AddRange(dataList.ToArray());

                #endregion
            }
            catch (Exception ex)
            {
                perforPeragainallotRepository.Update(againAllot, p => { p.States = 4; p.Remark = ex.ToString(); });
            }
            perforPeragainallotRepository.Update(againAllot, p => { p.States = 3; });
            return true;
            //return SheetFormat(perAgainExcel, situation);
        }

        /// <summary>
        /// 绩效详情表格转换
        /// </summary>
        /// <param name="perAgainExcel"></param>
        /// <param name="situation"></param>
        /// <returns></returns>
        private (SheetExportResponse SheetExport, PerAgainSituation AgainSituation) SheetFormat(PerAgainExcel perAgainExcel, PerAgainSituation situation)
        {
            #region 表格显示
            SheetExportResponse response = new SheetExportResponse("二次绩效分配表");
            //返回表格展示数据结构
            var row = new Row(0);
            row.Data.Add(new Cell(1, "姓名", 2, 1, false, false));
            row.Data.Add(new Cell(2, "职务", 2, 1, false, false));
            row.Data.Add(new Cell(3, "职称系数", 2, 1, false, false));
            row.Data.Add(new Cell(4, "出勤", 2, 1, false, false));
            row.Data.Add(new Cell(5, "职称出勤系数", 2, 1, false, false));
            row.Data.Add(new Cell(6, "年资", 2, 1, false, false));
            row.Data.Add(new Cell(7, "年资出勤系数", 2, 1, false, false));
            row.Data.Add(new Cell(8, "职称出勤绩效", 2, 1, false, false));
            row.Data.Add(new Cell(9, perAgainExcel.Header.CellValue, 1, perAgainExcel.Header.Children.Count, false, false));
            row.Data.Add(new Cell(9 + perAgainExcel.Header.Children.Count, "重点奖励", 2, 1, false, false));
            row.Data.Add(new Cell(10 + perAgainExcel.Header.Children.Count, "管理津贴", 2, 1, false, false));
            row.Data.Add(new Cell(11 + perAgainExcel.Header.Children.Count, "单独核算人员绩效", 2, 1, false, false));
            row.Data.Add(new Cell(12 + perAgainExcel.Header.Children.Count, "应发绩效工资金额", 2, 1, false, false));
            row.Data.Add(new Cell(13 + perAgainExcel.Header.Children.Count, "夜班费", 2, 1, false, false));
            row.Data.Add(new Cell(14 + perAgainExcel.Header.Children.Count, "实发工资合计", 2, 1, false, false));

            var rowTwo = new Row(1);
            int startpoint = 9;
            perAgainExcel.Header.Children.ForEach(t =>
            {
                rowTwo.Data.Add(new Cell(startpoint, t.CellValue, 1, 1, false, false));
                startpoint++;
            });
            response.Header.Add(row);
            response.Header.Add(rowTwo);

            for (int i = 0; i < perAgainExcel.AgainEmployee.Count(); i++)
            {
                var item = perAgainExcel.AgainEmployee.ElementAt(i);
                var rowbody = new Row(i);
                rowbody.Data.Add(new Cell(1, item.Name, 1, 1, false, false));
                rowbody.Data.Add(new Cell(2, item.JobTitle, 1, 1, false, false));
                rowbody.Data.Add(new Cell(3, item.JobFactor, 1, 1, false, false));
                rowbody.Data.Add(new Cell(4, item.Attendance, 1, 1, false, false));
                rowbody.Data.Add(new Cell(5, item.JobAttendanceFactor, 1, 1, false, false));
                rowbody.Data.Add(new Cell(6, item.YearFactor, 1, 1, false, false));
                rowbody.Data.Add(new Cell(7, item.YearAttendanceFactor, 1, 1, false, false));
                rowbody.Data.Add(new Cell(8, item.JobAttendancePerfor, 1, 1, false, false));

                var againList = perAgainExcel.AgainData.Where(t => t.RowNumber == item.RowNumber);
                startpoint = 9;
                perAgainExcel.Header.Children.ForEach(t =>
                {
                    var cellValue = againList.FirstOrDefault(s => t.SignID == s.SignID)?.CellValue;
                    rowbody.Data.Add(new Cell(startpoint, cellValue, 1, 1, false, false));
                    startpoint++;
                });
                rowbody.Data.Add(new Cell(9 + perAgainExcel.Header.Children.Count, item.Award, 1, 1, false, false));
                rowbody.Data.Add(new Cell(10 + perAgainExcel.Header.Children.Count, item.Allowance, 1, 1, false, false));
                rowbody.Data.Add(new Cell(11 + perAgainExcel.Header.Children.Count, item.AlonePerfor, 1, 1, false, false));
                rowbody.Data.Add(new Cell(12 + perAgainExcel.Header.Children.Count, item.GiveFee, 1, 1, false, false));
                rowbody.Data.Add(new Cell(13 + perAgainExcel.Header.Children.Count, item.NightShift, 1, 1, false, false));
                rowbody.Data.Add(new Cell(14 + perAgainExcel.Header.Children.Count, item.RealGiveFee, 1, 1, false, false));
                response.Row.Add(rowbody);
            }
            #endregion

            return (response, situation);
        }

        /// <summary>
        /// 查看二次分配详情及概览
        /// </summary>
        /// <param name="request"></param>
        /// <param name="user"></param>
        /// <returns></returns>
        public (SheetExportResponse SheetExport, PerAgainSituation AgainSituation) Detail(AgainAllotRequest request)
        {
            var againAllot = perforPeragainallotRepository.GetEntity(t => t.ID == request.AgainAllotID);
            if (againAllot == null || againAllot.ID == 0)
                throw new PerformanceException("绩效二次分配不存在");

            var situation = perforAgagainsituationRepository.GetEntity(t => t.AgainAllotID == againAllot.ID);
            var againsituation = Mapper.Map<PerAgainSituation>(situation);

            var againEmployee = perforAgemployeeRepository.GetEntities(t => t.AgainAllotID == againAllot.ID);
            //var employeeList = Mapper.Map<List<PerAgainEmployee>>(againEmployee);

            var header = perforAgheaderRepository.GetEntities(t => t.AgainAllotID == againAllot.ID);
            //var headerList = Mapper.Map<List<PerHeader>>(header);

            var data = perforAgdataRepository.GetEntities(t => t.AgainAllotID == againAllot.ID);
            //var dataList = Mapper.Map<List<PerAgainData>>(data);

            var pHead = header.FirstOrDefault(t => t.CellValue == "工作量绩效工资");
            var head = Mapper.Map<PerHeader>(pHead);
            var cHead = Mapper.Map<List<PerHeader>>(header.Where(t => t.CellValue != "工作量绩效工资"));
            head.Children = cHead;

            var perAgainExcel = new PerAgainExcel
            {
                Header = head,
                AgainData = Mapper.Map<List<PerAgainData>>(data),
                AgainEmployee = Mapper.Map<List<PerAgainEmployee>>(againEmployee)
            };

            return SheetFormat(perAgainExcel, againsituation);
        }


        /// <summary>
        /// 二次绩效新增记录
        /// </summary>
        /// <param name="allot"></param>
        /// <param name="userid"></param>
        /// <returns></returns>
        public bool Update(per_allot allot, int againid)
        {
            var again = perforPeragainallotRepository.GetEntity(t => t.ID == againid);
            again.ID = againid;
            again.UploadDateTime = DateTime.Now;
            again.Path = allot.Path;
            again.Remark = allot.Remark;
            again.States = 1;
            return perforPeragainallotRepository.Update(again);
        }

        /// <summary>
        /// 根据人物角色获取绩效列表
        /// </summary>
        /// <returns></returns>
        public List<AgainAllotResponse> GetAllotList(int userid)
        {
            var user = perforUserRepository.GetEntity(t => t.ID == userid);
            if (user == null)
                throw new NotImplementedException("人员ID无效");
            var hospital = perforUserhospitalRepository.GetEntity(t => t.UserID == userid);
            if (hospital == null)
                throw new NotImplementedException("人员未选择医院");
            var allot = perforPerallotRepository.GetEntities(t => t.HospitalId == hospital.HospitalID && t.States == 6);
            if (allot == null)
                throw new NotImplementedException("该医院未生成绩效");

            var allotId = allot.Select(t => t.ID).ToList();
            var again = perforPeragainallotRepository.GetEntities(t => allotId.Contains(t.AllotID.Value) && t.Department == user.Department);
            var arrId = again == null ? new List<int>() : again.Select(t => t.AllotID.Value).ToList();
            //取得未生成二次绩效的绩效id  
            var exceptId = allotId.Except(arrId).ToList();
            if (exceptId.Count > 0)
            {
                allot = allot.Where(t => exceptId.Contains(t.ID)).ToList();
                var model = allot.Select(t => new per_againallot
                {
                    AllotID = t.ID,
                    CreateUser = userid,
                    CreateDateTime = DateTime.Now,
                    Department = user.Department,
                    States = 0
                });
                //生成二次绩效
                perforPeragainallotRepository.AddRange(model.ToArray());
            }

            again = perforPeragainallotRepository.GetEntities(t => allotId.Contains(t.AllotID.Value) && t.CreateUser == userid);
            List<AgainAllotResponse> list = Mapper.Map<List<AgainAllotResponse>>(again);
            list.ForEach(t =>
            {
                var data = allot.Where(p => p.ID == t.AllotID).FirstOrDefault();
                t.Year = data.Year;
                t.Month = data.Month;
            });
            return list.OrderByDescending(t => t.Year).ThenByDescending(t => t.Month).ToList();
        }

        /// <summary>
        /// 获取状态不是归档的二次绩效的记录
        /// </summary>
        /// <param name="againid"></param>
        /// <returns></returns>
        public per_againallot GetAgainallot(int againid)
        {
            var list = perforPeragainallotRepository.GetEntity(t => t.ID == againid && t.States != 5);
            return list;
        }
    }
}
