using AutoMapper;
using Microsoft.Extensions.Logging;
using NPOI.HSSF.UserModel;
using NPOI.SS.UserModel;
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;
using System.Text;

namespace Performance.Services.AllotCompute
{
    /// <summary>
    /// 导入excel数据并保持
    /// </summary>
    public class ImportDataService : IAutoInjection
    {
        private PerSheetService perSheetService;
        private PerforPersheetRepository perforPerSheetRepository;
        private PerforPerallotRepository perforPerallotRepository;
        private PerforImdataRepository perforImDataRepository;
        private PerforImheaderRepository perforImHeaderRepository;
        private PerforImemployeeRepository perforImEmployeeRepository;
        private PerforImaccountbasicRepository perforImaccountbasicRepository;
        private PerforImspecialunitRepository perforImspecialunitRepository;
        private PerforImemployeeclinicRepository perforImemployeeclinicRepository;
        private PerforImemployeelogisticsRepository perforImemployeelogisticsRepository;
        //private PerforLogdbugRepository logdbug;
        private readonly LogManageService logManageService;
        private ILogger<ImportDataService> logger;

        public ImportDataService(PerSheetService perSheetService,
            PerforPersheetRepository perforPerSheetRepository,
            PerforPerallotRepository perforPerallotRepository,
            PerforImdataRepository perforImDataRepository,
            PerforImheaderRepository perforImHeaderRepository,
            PerforImemployeeRepository perforImEmployeeRepository,
            PerforImaccountbasicRepository perforImaccountbasicRepository,
            PerforImspecialunitRepository perforImspecialunitRepository,
            PerforImemployeeclinicRepository perforImemployeeclinicRepository,
            PerforImemployeelogisticsRepository perforImemployeelogisticsRepository,
            //PerforLogdbugRepository logdbug
            LogManageService logManageService,
            ILogger<ImportDataService> logger)
        {
            this.perSheetService = perSheetService;
            this.perforPerSheetRepository = perforPerSheetRepository;
            this.perforPerallotRepository = perforPerallotRepository;
            this.perforImDataRepository = perforImDataRepository;
            this.perforImHeaderRepository = perforImHeaderRepository;
            this.perforImEmployeeRepository = perforImEmployeeRepository;
            this.perforImaccountbasicRepository = perforImaccountbasicRepository;
            this.perforImspecialunitRepository = perforImspecialunitRepository;
            this.perforImemployeeclinicRepository = perforImemployeeclinicRepository;
            this.perforImemployeelogisticsRepository = perforImemployeelogisticsRepository;
            //this.logdbug = logdbug;
            this.logManageService = logManageService;
            this.logger = logger;
        }

        /// <summary>
        /// 读取excel并保存
        /// </summary>
        /// <param name="allot"></param>
        /// <returns></returns>
        public PerExcel ReadDataAndSave(per_allot allot)
        {
            var excel = Import(allot);
            Save(excel, allot.ID);
            var item = excel.PerSheet.FirstOrDefault(w => w.SheetType == SheetType.AccountBasicSpecial);
            if (item != null)
                excel.PerSheet.Remove(item);
            return excel;
        }

        /// <summary>
        /// 导入excel数据
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        private PerExcel Import(per_allot allot)
        {
            var path = allot.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));
                bool isnew = false;
                for (int i = 0; i < workbook.NumberOfSheets; i++)
                {
                    var sheet = workbook.GetSheetAt(i);
                    var sheetType = perSheetService.GetSheetType(sheet.SheetName);
                    // 保留临床科室测算表全部结构，用作展示，不做其他用途
                    if (sheetType == SheetType.AccountBasic)
                    {
                        var st = perSheetService.SheetAccountBasicSpecial(sheet, isnew);
                        excel.PerSheet.Add(st);
                    }
                    if (SheetType.Unidentifiable != sheetType)
                    {
                        logManageService.WriteMsg("开始读取excel文件", $"开始读取sheet -- {sheet.SheetName}", 1, allot.ID, "ReceiveMessage", true);
                        try
                        {
                            var st = perSheetService.Sheet(sheet, isnew);
                            excel.PerSheet.Add(st);
                        }
                        catch (Exception ex)
                        {
                            logManageService.WriteMsg("读取excel文件", ex.Message.ToString(), 4, allot.ID, "ReceiveMessage", true);
                            throw ex;
                        }
                    }
                    else
                    {
                        if (sheet.SheetName.Contains("开单医院"))
                            isnew = true;
                    }
                }
                logManageService.WriteMsg("开始读取excel文件", $"excel文件基础数据读取完成!", 1, allot.ID, "ReceiveMessage", true);
                return excel;
            }
        }

        /// <summary>
        /// 保存医院人员
        /// </summary>
        /// <param name="sheet"></param>
        /// <param name="allotId"></param>
        /// <returns></returns>
        private int SaveEmployee(PerSheet sheet, int allotId)
        {
            var imsheet = new per_sheet { AllotID = allotId, SheetName = sheet.SheetName, Source = 1, SheetType = (int)sheet.SheetType };
            perforPerSheetRepository.Add(imsheet);
            var dataList = sheet.PerData.Select(t => (PerDataEmployee)t);
            //新上传名单中无人员名单，取到最后日期的数据
            if (dataList == null || dataList.Count() <= 0)
            {
                var allot = perforPerallotRepository.GetEntity(t => t.ID == allotId);
                var allotList = perforPerallotRepository.GetEntities(t => t.HospitalId == allot.HospitalId)
                                .OrderByDescending(t => t.Year).ThenByDescending(t => t.Month);
                foreach (var item in allotList)
                {
                    var employeeList = perforImEmployeeRepository.GetEntities(t => t.AllotID == item.ID);
                    if (employeeList != null && employeeList.Count > 0)
                    {
                        dataList = Mapper.Map<List<PerDataEmployee>>(employeeList);
                        break;
                    }
                }
            }
            List<im_employee> addList = new List<im_employee>();
            foreach (var data in dataList)
            {
                var imdata = Mapper.Map<im_employee>(data);
                imdata.SheetID = imsheet.ID;
                imdata.AllotID = allotId;
                //imdata.OtherPerfor = data.OthePerfor;
                addList.Add(imdata);
            }
            perforImEmployeeRepository.AddRange(addList.ToArray());
            return imsheet.ID;
        }

        /// <summary>
        /// 保存临床人员
        /// </summary>
        /// <param name="sheet"></param>
        /// <param name="allotId"></param>
        /// <returns></returns>
        private int SaveClinicEmployee(PerSheet sheet, int allotId)
        {
            var imsheet = new per_sheet { AllotID = allotId, SheetName = sheet.SheetName, Source = 1, SheetType = (int)sheet.SheetType };
            perforPerSheetRepository.Add(imsheet);
            var dataList = sheet.PerData.Select(t => (PerDataClinicEmployee)t);
            //新上传名单中无人员名单，取到最后日期的数据
            if (dataList == null || dataList.Count() <= 0)
            {
                var allot = perforPerallotRepository.GetEntity(t => t.ID == allotId);
                var allotList = perforPerallotRepository.GetEntities(t => t.HospitalId == allot.HospitalId)
                                .OrderByDescending(t => t.Year).ThenByDescending(t => t.Month);
                foreach (var item in allotList)
                {
                    var employeeList = perforImemployeeclinicRepository.GetEntities(t => t.AllotID == item.ID);
                    if (employeeList != null && employeeList.Count > 0)
                    {
                        dataList = Mapper.Map<List<PerDataClinicEmployee>>(employeeList);
                        break;
                    }
                }
            }
            List<im_employee_clinic> addList = new List<im_employee_clinic>();
            foreach (var data in dataList)
            {
                var imdata = Mapper.Map<im_employee_clinic>(data);
                imdata.SheetID = imsheet.ID;
                imdata.AllotID = allotId;
                imdata.OtherPerfor = data.OthePerfor;
                addList.Add(imdata);
            }
            perforImemployeeclinicRepository.AddRange(addList.ToArray());
            return imsheet.ID;
        }

        /// <summary>
        /// 保存临床人员
        /// </summary>
        /// <param name="sheet"></param>
        /// <param name="allotId"></param>
        /// <returns></returns>
        private int SaveLogisticsEmployee(PerSheet sheet, int allotId)
        {
            var imsheet = new per_sheet { AllotID = allotId, SheetName = sheet.SheetName, Source = 1, SheetType = (int)sheet.SheetType };
            perforPerSheetRepository.Add(imsheet);
            var dataList = sheet.PerData.Select(t => (PerDataLogisticsEmployee)t);
            //新上传名单中无人员名单，取到最后日期的数据
            if (dataList == null || dataList.Count() <= 0)
            {
                var allot = perforPerallotRepository.GetEntity(t => t.ID == allotId);
                var allotList = perforPerallotRepository.GetEntities(t => t.HospitalId == allot.HospitalId)
                                .OrderByDescending(t => t.Year).ThenByDescending(t => t.Month);
                foreach (var item in allotList)
                {
                    var employeeList = perforImemployeelogisticsRepository.GetEntities(t => t.AllotID == item.ID);
                    if (employeeList != null && employeeList.Count > 0)
                    {
                        dataList = Mapper.Map<List<PerDataLogisticsEmployee>>(employeeList);
                        break;
                    }
                }
            }
            List<im_employee_logistics> addList = new List<im_employee_logistics>();
            foreach (var data in dataList)
            {
                var imdata = Mapper.Map<im_employee_logistics>(data);
                imdata.SheetID = imsheet.ID;
                imdata.AllotID = allotId;
                imdata.OtherPerfor = data.OthePerfor;
                addList.Add(imdata);
            }
            perforImemployeelogisticsRepository.AddRange(addList.ToArray());
            return imsheet.ID;
        }

        /// <summary>
        /// 保存科室绩效基础表
        /// </summary>
        /// <param name="sheet"></param>
        /// <param name="allotId"></param>
        /// <returns></returns>
        private int SaveAccountBasic(PerSheet sheet, int allotId)
        {
            //var imsheet = new per_sheet { AllotID = allotId, SheetName = sheet.SheetName, Source = 1, SheetType = (int)sheet.SheetType };
            //perforPerSheetRepository.Add(imsheet);
            List<int> types = new List<int> { (int)SheetType.AccountBasicSpecial, (int)SheetType.AccountBasic };
            var sheets = perforPerSheetRepository.GetEntities(w => w.AllotID == allotId && w.SheetType.HasValue && types.Contains(w.SheetType.Value));
            var imsheet = sheets?.FirstOrDefault();
            if (imsheet != null)
            {
                imsheet.SheetType = (int)SheetType.AccountBasic;
                perforPerSheetRepository.Update(imsheet);
            }
            else
            {
                imsheet = new per_sheet { AllotID = allotId, SheetName = sheet.SheetName, Source = 1, SheetType = (int)sheet.SheetType };
                perforPerSheetRepository.Add(imsheet);
            }

            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());
            return imsheet.ID;
        }

        /// <summary>
        /// 保存特殊科室
        /// </summary>
        /// <param name="sheet"></param>
        /// <param name="allotId"></param>
        /// <returns></returns>
        private int SaveSpecialUnit(PerSheet sheet, int allotId)
        {
            var imsheet = new per_sheet { AllotID = allotId, SheetName = sheet.SheetName, Source = 1, SheetType = (int)sheet.SheetType };
            perforPerSheetRepository.Add(imsheet);

            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());
            return imsheet.ID;
        }

        /// <summary>
        /// 保存通用格式
        /// </summary>
        /// <param name="sheet"></param>
        /// <param name="allotId"></param>
        /// <returns></returns>
        private int SaveCommon(PerSheet sheet, int allotId)
        {
            per_sheet imsheet = null;
            List<int> types = new List<int> { (int)SheetType.AccountBasicSpecial, (int)SheetType.AccountBasic };
            if (types.Any(type => type == (int)sheet.SheetType))
            {
                var sheets = perforPerSheetRepository.GetEntities(w => w.AllotID == allotId && w.SheetType.HasValue && types.Contains(w.SheetType.Value));
                imsheet = sheets?.FirstOrDefault();
                if (imsheet != null)
                {
                    imsheet.SheetType = (int)SheetType.AccountBasic;
                    perforPerSheetRepository.Update(imsheet);
                }
            }
            if (imsheet == null)
            {
                imsheet = new per_sheet { AllotID = allotId, SheetName = sheet.SheetName, Source = 1, SheetType = (int)sheet.SheetType };
                perforPerSheetRepository.Add(imsheet);
            }


            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);
            }
            if (addDataList != null && addDataList.Any())
            {
                int rows = 500;
                for (int i = 0; i < Math.Ceiling((double)addDataList.Count / rows); i++)
                {
                    perforImDataRepository.AddRange(addDataList.Skip(rows * i).Take(rows).ToArray());
                }
            }
            return imsheet.ID;
        }

        private void SaveHeader(PerSheet sheet, int allotId, int imsheetid)
        {
            List<im_header> addHeadList = new List<im_header>();
            foreach (var header in sheet.PerHeader)
            {
                var imheader = Mapper.Map<im_header>(header);
                imheader.SheetID = imsheetid;
                imheader.AllotID = allotId;
                perforImHeaderRepository.Add(imheader);
                if (header.IsHasChildren)
                {
                    foreach (var child in header.Children)
                    {
                        var imheaderChild = Mapper.Map<im_header>(child);
                        imheaderChild.SheetID = imsheetid;
                        imheaderChild.ParentID = imheader.ID;
                        imheaderChild.AllotID = allotId;
                        addHeadList.Add(imheaderChild);
                    }
                }
            }
            if (addHeadList != null && addHeadList.Any())
                perforImHeaderRepository.AddRange(addHeadList.ToArray());
        }

        private bool Save(PerExcel excel, int allotId)
        {
            foreach (var sheet in excel.PerSheet)
            {
                logManageService.WriteMsg("保存基础数据", $"开始保存数据 -- {sheet.SheetName}", 1, allotId, "ReceiveMessage", true);
                if (sheet.SheetType == SheetType.Employee)
                {
                    var imsheetid = SaveEmployee(sheet, allotId);
                    SaveHeader(sheet, allotId, imsheetid);
                }
                else if (sheet.SheetType == SheetType.LogisticsEmployee)
                {
                    var imsheetid = SaveLogisticsEmployee(sheet, allotId);
                    SaveHeader(sheet, allotId, imsheetid);
                }
                else if (sheet.SheetType == SheetType.ClinicEmployee)
                {
                    var imsheetid = SaveClinicEmployee(sheet, allotId);
                    SaveHeader(sheet, allotId, imsheetid);
                }
                else if (sheet.SheetType == SheetType.AccountBasic)
                {
                    var imsheetid = SaveAccountBasic(sheet, allotId);
                    SaveHeader(sheet, allotId, imsheetid);
                }
                else if (sheet.SheetType == SheetType.SpecialUnit)
                {
                    var imsheetid = SaveSpecialUnit(sheet, allotId);
                    SaveHeader(sheet, allotId, imsheetid);
                }
                else
                {
                    var imsheetid = SaveCommon(sheet, allotId);
                    SaveHeader(sheet, allotId, imsheetid);
                }
            }
            logManageService.WriteMsg("保存基础数据", $"基础数据保存完成!", 1, allotId, "ReceiveMessage", true);
            return true;
        }
    }
}
