using AutoMapper;
using NPOI.HSSF.UserModel;
using NPOI.SS.UserModel;
using NPOI.SS.Util;
using NPOI.XSSF.UserModel;
using Performance.DtoModels;
using Performance.EntityModels;
using Performance.Infrastructure;
using Performance.Repository;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

namespace Performance.Services
{
    public class PerExcelService : IAutoInjection
    {
        private PerSheetService _perSheetService;
        private PerHeaderService _perHeaderService;
        private PerforPerSheetRepository _perforImSheetRepository;
        private PerforImDataRepository _perforImDataRepository;
        private PerforImHeaderRepository _perforImHeaderRepository;
        private PerforImEmployeeRepository _perforImEmployeeRepository;
        private PerforImaccountbasicRepository _perforImaccountbasicRepository;
        private PerforResAccountnurseRepository _perforResAccountnurseRepository;
        private PerforCofdirectorRepository _perforCofdirectorRepository;
        private PerforCofdrugpropRepository _perforCofdrugpropRepository;
        private PerforCofincomeRepository _perforCofincomeRepository;
        private PerforCofworkyearRepository _perforCofworkyearRepository;
        private PerforResAccountdoctorRepository _perforResAccountdoctorRepository;
        private PerforResbaiscnormRepository _perforResbaiscnormRepository;
        private PerforRescomputeRepository _perforRescomputeRepository;
        private PerforImspecialunitRepository _perforImspecialunitRepository;
        private PerforResspecialunitRepository _perforResspecialunitRepository;
        private PerforPerAllotRepository _perforPerAllotRepository;
        public PerExcelService(PerSheetService perSheetService,
            PerHeaderService perHeaderService,
            PerforPerSheetRepository perforImSheetRepository,
            PerforImDataRepository perforImDataRepository,
            PerforImHeaderRepository perforImHeaderRepository,
            PerforImEmployeeRepository perforImEmployeeRepository,
            PerforImaccountbasicRepository perforImaccountbasicRepository,
            PerforResAccountnurseRepository perforResAccountnurseRepository,
            PerforCofdirectorRepository perforCofdirectorRepository,
            PerforCofdrugpropRepository perforCofdrugpropRepository,
            PerforCofincomeRepository perforCofincomeRepository,
            PerforResAccountdoctorRepository perforResAccountdoctorRepository,
            PerforResbaiscnormRepository perforResbaiscnormRepository,
            PerforRescomputeRepository perforRescomputeRepository,
            PerforCofworkyearRepository perforCofworkyearRepository,
            PerforImspecialunitRepository perforImspecialunitRepository,
            PerforResspecialunitRepository perforResspecialunitRepository,
            PerforPerAllotRepository perforPerAllotRepository)
        {
            _perSheetService = perSheetService;
            _perHeaderService = perHeaderService;
            _perforImSheetRepository = perforImSheetRepository;
            _perforImDataRepository = perforImDataRepository;
            _perforImHeaderRepository = perforImHeaderRepository;
            _perforImEmployeeRepository = perforImEmployeeRepository;
            _perforImaccountbasicRepository = perforImaccountbasicRepository;
            _perforResAccountnurseRepository = perforResAccountnurseRepository;
            _perforResAccountdoctorRepository = perforResAccountdoctorRepository;
            _perforCofdirectorRepository = perforCofdirectorRepository;
            _perforCofdrugpropRepository = perforCofdrugpropRepository;
            _perforCofincomeRepository = perforCofincomeRepository;
            _perforCofworkyearRepository = perforCofworkyearRepository;
            _perforResbaiscnormRepository = perforResbaiscnormRepository;
            _perforRescomputeRepository = perforRescomputeRepository;
            _perforPerAllotRepository = perforPerAllotRepository;
            _perforImspecialunitRepository = perforImspecialunitRepository;
            _perforResspecialunitRepository = perforResspecialunitRepository;
        }

        public void Execute(per_allot allot)
        {
            try
            {
                allot.States = (int)AllotStates.InCheckData;
                allot.Remark = EnumHelper.GetDescription(AllotStates.InCheckData);
                _perforPerAllotRepository.Update(allot);

                Clear(allot.ID);

                // 导出数据
                var excel = Import(allot.Path);

                // 保存数据
                Save(excel.PerSheet, allot.ID, 1);

                Check();

                allot.States = (int)AllotStates.InGenerate;
                allot.Remark = EnumHelper.GetDescription(AllotStates.InGenerate);
                _perforPerAllotRepository.Update(allot);

                // 计算合并数据
                List<PerSheet> list = ProcessCompute(excel);

                // 保存过程数据
                Save(list, allot.ID, 2);

                var baiscnorm = NurseBaiscnorm(list);

                // 计算最总数据
                var baiscnormList = Compute(allot, excel, baiscnorm);
                SpecialUnitCompute(excel, allot, baiscnormList);

                //发送邮件
                SendEmail(allot);
            }
            catch (Exception ex)
            {
                allot.States = (int)AllotStates.GenerateFail;
                allot.Remark = EnumHelper.GetDescription(AllotStates.GenerateFail);
                _perforPerAllotRepository.Update(allot);
            }

            allot.States = (int)AllotStates.GenerateSucceed;
            allot.Remark = EnumHelper.GetDescription(AllotStates.GenerateSucceed);
            _perforPerAllotRepository.Update(allot);
        }

        private void Check()
        {

        }

        /// <summary>
        /// 特殊科室绩效计算
        /// </summary>
        /// <param name="excel"></param>
        /// <param name="allot"></param>
        private void SpecialUnitCompute(PerExcel excel, per_allot allot, List<res_baiscnorm> baiscnormList)
        {
            var specialUnit = excel.PerSheet.FirstOrDefault(t => t.SheetType == SheetType.SpecialUnit);
            if (specialUnit == null || specialUnit.PerData.Count == 0)
                return;
            var dataList = specialUnit.PerData.Select(t => (PerDataSpecialUnit)t);
            BaiscNormService baiscNormService = new BaiscNormService();

            var typeList = EnumHelper.GetItems<PerformanceType>();
            List<res_specialunit> resDataList = new List<res_specialunit>();
            foreach (var t in dataList)
            {
                var type = typeList.FirstOrDefault(o => o.Description == t.QuantitativeIndicators);
                if (type != null)
                    t.QuantitativeIndicatorsValue = baiscNormService.GetBaiscNorm(baiscnormList, (PerformanceType)type.Value);

                var res = new res_specialunit
                {
                    AllotID = allot.ID,
                    AccountingUnit = t.AccountingUnit,
                    Department = t.AccountingUnit,
                    Number = t.Number,
                    QuantitativeIndicators = t.QuantitativeIndicators,
                    Quantity = t.Quantity,
                    QuantitativeIndicatorsValue = t.QuantitativeIndicatorsValue,
                    ScoringAverage = t.ScoringAverage,
                    OtherPerfor = t.OtherPerfor,
                    Punishment = t.Punishment,
                    Adjust = t.Adjust,
                    Avg = t.Quantity * t.QuantitativeIndicatorsValue / t.Number,
                    ShouldFee = t.Quantity * t.QuantitativeIndicatorsValue,
                    GiveFee = (t.Quantity * t.QuantitativeIndicatorsValue + t.OtherPerfor + t.Punishment) * t.Adjust,
                };
                resDataList.Add(res);
            }
            _perforResspecialunitRepository.AddRange(resDataList.ToArray());
        }

        /// <summary>
        /// 获取临床护士平均绩效
        /// </summary>
        /// <param name="list"></param>
        /// <returns></returns>
        private res_baiscnorm NurseBaiscnorm(List<PerSheet> list)
        {
            var sheet = list.FirstOrDefault(t => t.SheetType == SheetType.ComputeNurseAccount);
            var perdata = sheet.PerData.Select(t => (PerDataAccountNurse)t);
            return new res_baiscnorm
            {
                PositionName = "临床护士",
                TotelNumber = perdata.Sum(t => t.Number),
                TotelValue = perdata.Sum(t => t.PerforTotal),
                AvgValue = perdata.Sum(t => t.PerforTotal) / perdata.Sum(t => t.Number)
            };
        }

        private void SendEmail(per_allot allot)
        {

        }

        public void Clear(int allotId)
        {
            var director = _perforCofdirectorRepository.GetEntities(t => t.AllotID == allotId);
            if (director != null)
                _perforCofdirectorRepository.RemoveRange(director.ToArray());
            var durgprop = _perforCofdrugpropRepository.GetEntities(t => t.AllotID == allotId);
            if (durgprop != null)
                _perforCofdrugpropRepository.RemoveRange(durgprop.ToArray());
            var income = _perforCofincomeRepository.GetEntities(t => t.AllotID == allotId);
            if (income != null)
                _perforCofincomeRepository.RemoveRange(income.ToArray());
            var workyear = _perforCofworkyearRepository.GetEntities(t => t.AllotID == allotId);
            if (workyear != null)
                _perforCofworkyearRepository.RemoveRange(workyear.ToArray());
        }

        /// <summary>
        /// 导入excel数据
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public PerExcel Import(string path)
        {
            PerExcel excel = new PerExcel
            {
                Path = path,
                FileName = FileHelper.GetFileNameNoExtension(path),
                Version = FileHelper.GetExtension(path) == ".xlsx" ? ExcelVersion.xlsx : ExcelVersion.xls,
                PerSheet = new List<PerSheet>()
            };
            using (FileStream fs = new FileStream(path, FileMode.Open))
            {
                IWorkbook workbook = (excel.Version == ExcelVersion.xlsx)
                    ? (IWorkbook)(new XSSFWorkbook(fs))
                    : (IWorkbook)(new HSSFWorkbook(fs));
                for (int i = 0; i < workbook.NumberOfSheets; i++)
                {
                    var sheet = workbook.GetSheetAt(i);
                    if (SheetType.Unidentifiable != _perSheetService.GetSheetType(sheet.SheetName))
                    {
                        var st = _perSheetService.Sheet(sheet);
                        excel.PerSheet.Add(st);
                    }
                }
                return excel;
            }
        }


        /// <summary>
        /// 复制报表基础配置
        /// </summary>
        /// <param name="iD"></param>
        public void Copy(per_allot allot)
        {
            var list = _perforPerAllotRepository.GetEntities(t => t.HospitalId == allot.HospitalId)
                        .OrderBy(t => t.Year).ThenBy(t => t.Month).ToList();
            int allotId = 0;
            for (int i = 0; i < list.Count; i++)
            {
                if (list[i].ID == allot.ID && (i - 1) >= 0)
                    allotId = list[i - 1].ID;
            }
            var director = _perforCofdirectorRepository.GetEntities(t => t.AllotID == allotId) ?? _perforCofdirectorRepository.GetEntities(t => t.AllotID == -1);
            var newDirectors = director.Select(t => new cof_director { AllotID = allot.ID, JobTitle = t.JobTitle, TypeName = t.TypeName, Value = t.Value });
            _perforCofdirectorRepository.AddRange(newDirectors.ToArray());

            var durgprop = _perforCofdrugpropRepository.GetEntities(t => t.AllotID == allotId) ?? _perforCofdrugpropRepository.GetEntities(t => t.AllotID == -1);
            var newDurgprops = durgprop.Select(t => new cof_drugprop { AllotID = allot.ID, MaxRange = t.MaxRange, MinRange = t.MinRange, Value = t.Value });
            _perforCofdrugpropRepository.AddRange(newDurgprops.ToArray());

            var income = _perforCofincomeRepository.GetEntities(t => t.AllotID == allotId) ?? _perforCofincomeRepository.GetEntities(t => t.AllotID == -1);
            var newIncomes = income.Select(t => new cof_income { AllotID = allot.ID, SheetNameKeyword = t.SheetNameKeyword, UnitName = t.UnitName, Value = t.Value });
            _perforCofincomeRepository.AddRange(newIncomes.ToArray());

            var workyear = _perforCofworkyearRepository.GetEntities(t => t.AllotID == allotId) ?? _perforCofworkyearRepository.GetEntities(t => t.AllotID == -1);
            var newWorkyears = workyear.Select(t => new cof_workyear { AllotID = allot.ID, MaxRange = t.MaxRange, MinRange = t.MinRange, Value = t.Value });
            _perforCofworkyearRepository.AddRange(newWorkyears.ToArray());
        }

        /// <summary>
        /// 计算绩效
        /// </summary>
        /// <param name="excel"></param>
        public List<PerSheet> ProcessCompute(PerExcel excel)
        {
            return _perSheetService.ProcessCompute(excel);
        }


        /// <summary>
        /// 保存绩效结果
        /// </summary>
        /// <param name="perSheets"></param>
        /// <param name="allotId"></param>
        /// <param name="source">数据来源 1 excel 导入 2 计算</param>
        public void Save(List<PerSheet> perSheets, int allotId, int source)
        {
            foreach (var sheet in perSheets)
            {
                var imsheet = new per_sheet { AllotID = allotId, SheetName = sheet.SheetName, Source = source, SheetType = (int)sheet.SheetType };
                _perforImSheetRepository.Add(imsheet);
                if (sheet.SheetType == SheetType.Employee)
                {
                    var dataList = sheet.PerData.Select(t => (PerDataEmployee)t);
                    foreach (var data in dataList)
                    {
                        var imdata = Mapper.Map<im_employee>(data);
                        imdata.SheetID = imsheet.ID;
                        imdata.AllotID = allotId;
                        _perforImEmployeeRepository.Add(imdata);
                    }
                }
                else if (sheet.SheetType == SheetType.AccountBasic)
                {
                    var dataList = sheet.PerData.Select(t => (PerDataAccountBaisc)t);
                    List<im_accountbasic> addList = new List<im_accountbasic>();
                    foreach (var data in dataList)
                    {
                        var imdata = Mapper.Map<im_accountbasic>(data);
                        imdata.SheetID = imsheet.ID;
                        imdata.AllotID = allotId;
                        addList.Add(imdata);
                    }
                    _perforImaccountbasicRepository.AddRange(addList.ToArray());
                }
                else if (sheet.SheetType == SheetType.SpecialUnit)
                {
                    var dataList = sheet.PerData.Select(t => (PerDataSpecialUnit)t);
                    List<im_specialunit> addList = new List<im_specialunit>();
                    foreach (var data in dataList)
                    {
                        var imdata = Mapper.Map<im_specialunit>(data);
                        imdata.SheetID = imsheet.ID;
                        imdata.AllotID = allotId;
                        addList.Add(imdata);
                    }
                    _perforImspecialunitRepository.AddRange(addList.ToArray());
                }
                else if (sheet.SheetType == SheetType.ComputeDoctorAccount)
                {
                    var dataList = sheet.PerData.Select(t => (PerDataAccountDoctor)t);
                    List<res_accountdoctor> addList = new List<res_accountdoctor>();
                    foreach (var data in dataList)
                    {
                        var imdata = Mapper.Map<res_accountdoctor>(data);
                        imdata.SheetID = imsheet.ID;
                        imdata.AllotID = allotId;
                        addList.Add(imdata);
                    }
                    _perforResAccountdoctorRepository.AddRange(addList.ToArray());
                }
                else if (sheet.SheetType == SheetType.ComputeNurseAccount)
                {
                    var dataList = sheet.PerData.Select(t => (PerDataAccountNurse)t);
                    List<res_accountnurse> addList = new List<res_accountnurse>();
                    foreach (var data in dataList)
                    {
                        var imdata = Mapper.Map<res_accountnurse>(data);
                        imdata.SheetID = imsheet.ID;
                        imdata.AllotID = allotId;
                        addList.Add(imdata);
                    }
                    _perforResAccountnurseRepository.AddRange(addList.ToArray());
                }
                else
                {
                    List<im_header> addHeadList = new List<im_header>();
                    foreach (var header in sheet.PerHeader)
                    {
                        var imheader = Mapper.Map<im_header>(header);
                        imheader.SheetID = imsheet.ID;
                        imheader.AllotID = allotId;
                        _perforImHeaderRepository.Add(imheader);
                        if (header.IsHasChildren)
                        {
                            foreach (var child in header.Children)
                            {
                                var imheaderChild = Mapper.Map<im_header>(child);
                                imheaderChild.SheetID = imsheet.ID;
                                imheaderChild.ParentID = imheader.ID;
                                imheaderChild.AllotID = allotId;
                                addHeadList.Add(imheaderChild);
                            }
                        }
                    }
                    _perforImHeaderRepository.AddRange(addHeadList.ToArray());

                    List<im_data> addDataList = new List<im_data>();
                    var dataList = sheet.PerData.Select(t => (PerData)t);
                    foreach (var data in dataList)
                    {
                        var imdata = Mapper.Map<im_data>(data);
                        imdata.SheetID = imsheet.ID;
                        imdata.AllotID = allotId;
                        addDataList.Add(imdata);
                    }
                    _perforImDataRepository.AddRange(addDataList.ToArray());
                }
            }

            //throw new NotImplementedException();
        }

        /// <summary>
        /// 计算最终数据
        /// </summary>
        /// <param name="excel"></param>
        public List<res_baiscnorm> Compute(per_allot allot, PerExcel excel, res_baiscnorm baiscnorm)
        {
            var sheetList = _perforImSheetRepository.GetEntities(t => t.AllotID == allot.ID);
            //取出人员信息           
            var empolyeeList = _perforImEmployeeRepository.GetEntities(t => t.AllotID == allot.ID);
            //年资系数
            var workyearList = _perforCofworkyearRepository.GetEntities();
            //规模绩效和效率绩效配置表
            var directorList = _perforCofdirectorRepository.GetEntities();
            //取出医生科室
            var doctorList = _perforResAccountdoctorRepository.GetEntities(t => t.AllotID == allot.ID);
            //取出护士科室
            var nurseList = _perforResAccountnurseRepository.GetEntities(t => t.AllotID == allot.ID);

            List<ComputeEmployee> computeEmployees = Mapper.Map<List<ComputeEmployee>>(empolyeeList);

            List<ComputeSource> computeSources = new List<ComputeSource>();
            computeSources.AddRange(Mapper.Map<List<ComputeSource>>(doctorList));
            computeSources.AddRange(Mapper.Map<List<ComputeSource>>(nurseList));

            ComputeDirector computeDirector = new ComputeDirector();
            var computResult = computeDirector.Compute(computeEmployees, computeSources, directorList);
            var baiscnormList = computeDirector.ComputeAvg(computResult);
            baiscnormList.Add(baiscnorm);

            var computResult2 = computeDirector.Compute(computeEmployees, baiscnormList, workyearList);


            var computes = Mapper.Map<List<res_compute>>(computResult);
            computes.AddRange(Mapper.Map<List<res_compute>>(computResult2));
            computes.ForEach(t => t.AllotID = allot.ID);
            _perforRescomputeRepository.AddRange(computes.ToArray());

            baiscnormList.ForEach(t => t.AllotID = allot.ID);
            _perforResbaiscnormRepository.AddRange(baiscnormList.ToArray());

            return baiscnormList;
        }
    }
}
