using Microsoft.Extensions.Logging;
using NPOI.SS.UserModel;
using Performance.DtoModels;
using Performance.EntityModels;
using Performance.Infrastructure;
using Performance.Repository;
using Performance.Services.ExtractExcelService;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;

namespace Performance.Services
{
    public class ReportGlobalService : IAutoInjection
    {
        private readonly ILogger logger;
        private readonly PerforPerallotRepository perallotRepository;
        private readonly PerforReportglobalRepository reportglobalRepository;
        private readonly PerforHisimportdataRepository hisimportdataRepository;

        public ReportGlobalService(
            ILogger<ReportGlobalService> logger,
            PerforPerallotRepository perallotRepository,
            PerforReportglobalRepository reportglobalRepository,
            PerforHisimportdataRepository hisimportdataRepository
            )
        {
            this.logger = logger;
            this.perallotRepository = perallotRepository;
            this.reportglobalRepository = reportglobalRepository;
            this.hisimportdataRepository = hisimportdataRepository;
        }

        #region Report_Global

        public List<report_global> GetReportGlobals(int hospitalId)
        {
            var globals = reportglobalRepository.GetEntities(t => t.HospitalID == hospitalId)
                ?.OrderBy(t => t.Category).ThenBy(t => t.Year).ThenBy(t => t.Month).ToList();
            return globals;
        }

        public bool CreateReportGlobal(report_global global)
        {
            return reportglobalRepository.Add(global);
        }

        public bool UpdateReportGlobal(report_global global)
        {
            var entity = reportglobalRepository.GetEntity(t => t.Id == global.Id);
            entity.Year = global.Year;
            entity.Month = global.Month;
            entity.Category = global.Category;
            entity.Value = global.Value;
            return reportglobalRepository.Update(entity);
        }

        public bool DeleteReportGlobal(int globalId)
        {
            var entity = reportglobalRepository.GetEntity(t => t.Id == globalId);
            return reportglobalRepository.Remove(entity);
        }

        #endregion Report_Global

        #region Copy Previous Report Data

        public void CopyPreviousGlobalData(per_allot allot)
        {
            var globals = reportglobalRepository.GetEntities(t => t.HospitalID == allot.HospitalId);
            if (globals == null || !globals.Any()) return;

            var onlyYears = globals.Where(t => t.Year.HasValue && !t.Month.HasValue);
            if (onlyYears != null && onlyYears.Any())
            {
                var year = onlyYears.Where(t => t.Year < allot.Year)?.Max(t => t.Year);
                if (year.HasValue)
                {
                    var current = onlyYears.Where(t => t.Year == allot.Year)?.ToList() ?? new List<report_global>();
                    var previous = onlyYears.Where(t => t.Year == year)?.ToList() ?? new List<report_global>();

                    var newdata = previous.Where(t => !current.Select(s => s.Category).Contains(t.Category))?.ToList();
                    if (newdata != null && newdata.Any())
                    {
                        newdata = newdata.Select(t => new report_global
                        {
                            HospitalID = allot.HospitalId,
                            Year = allot.Year,
                            Category = t.Category,
                            Value = t.Value
                        }).ToList();
                        reportglobalRepository.AddRange(newdata.ToArray());
                    }
                }
            }

            var yearAndMonth = globals.Where(t => t.Year.HasValue && t.Month.HasValue);
            if (yearAndMonth != null && yearAndMonth.Any())
            {
                var month = yearAndMonth.Where(t => t.Year == allot.Year && t.Month < allot.Month)?.Max(t => t.Month);
                if (allot.Month == 1 || !month.HasValue)
                {
                    var year = yearAndMonth.Where(t => t.Year < allot.Year)?.Max(t => t.Year);
                    month = yearAndMonth.Where(t => t.Year == year)?.Max(t => t.Month);
                    if (year.HasValue && month.HasValue)
                    {
                        var current = yearAndMonth.Where(t => t.Year == allot.Year && t.Month == allot.Month)?.ToList() ?? new List<report_global>();
                        var previous = yearAndMonth.Where(t => t.Year == year && t.Month == month)?.ToList() ?? new List<report_global>();

                        var newdata = previous.Where(t => !current.Select(s => s.Category).Contains(t.Category))?.ToList();
                        if (newdata != null && newdata.Any())
                        {
                            newdata = newdata.Select(t => new report_global
                            {
                                HospitalID = allot.HospitalId,
                                Year = allot.Year,
                                Month = allot.Month,
                                Category = t.Category,
                                Value = t.Value
                            }).ToList();
                            reportglobalRepository.AddRange(newdata.ToArray());
                        }
                    }
                }
                else
                {
                    if (month.HasValue)
                    {
                        var current = yearAndMonth.Where(t => t.Year == allot.Year && t.Month == allot.Month)?.ToList() ?? new List<report_global>();
                        var previous = yearAndMonth.Where(t => t.Year == allot.Year && t.Month == month)?.ToList() ?? new List<report_global>();

                        var newdata = previous.Where(t => !current.Select(s => s.Category).Contains(t.Category))?.ToList();
                        if (newdata != null && newdata.Any())
                        {
                            newdata = newdata.Select(t => new report_global
                            {
                                HospitalID = allot.HospitalId,
                                Year = allot.Year,
                                Month = allot.Month,
                                Category = t.Category,
                                Value = t.Value
                            }).ToList();
                            reportglobalRepository.AddRange(newdata.ToArray());
                        }
                    }
                }
            }
        }

        #endregion Copy Previous Report Data

        #region ImportFile && SaveData

        public void ImportAllotData(int hospitalId, string filePath)
        {
            IWorkbook workbook = null;
            try
            {
                workbook = ExcelHelper.GetWorkbook(filePath);
                if (workbook == null) throw new PerformanceException("文件读取失败");

                var allots = perallotRepository.GetEntities(t => t.HospitalId == hospitalId);

                for (int sheetIndex = 0; sheetIndex < workbook.NumberOfSheets; sheetIndex++)
                {
                    var sheet = workbook.GetSheetAt(sheetIndex);

                    if (sheet == null) continue;

                    string sheetName = sheet.SheetName.NoBlank();

                    var columns = GetColumns(sheet, sheetName, out string sourceType);
                    if (columns == null || !columns.Any(t => !string.IsNullOrEmpty(t.NoBlank()))) continue;

                    var data = new List<his_importdata>();
                    for (int rowIndex = 1; rowIndex < sheet.LastRowNum + 1; rowIndex++)
                    {
                        var row = sheet.GetRow(rowIndex);
                        if (row == null) continue;

                        his_importdata importdata = new his_importdata
                        {
                            Year = GetCellValue<int>(row, columns, nameof(his_importdata.Year)),
                            Month = GetCellValue<int>(row, columns, nameof(his_importdata.Month)),
                            AccountingUnit = GetCellValue<string>(row, columns, nameof(his_importdata.AccountingUnit)),
                            Department = GetCellValue<string>(row, columns, nameof(his_importdata.Department)),
                            PersonnelNumber = GetCellValue<string>(row, columns, nameof(his_importdata.PersonnelNumber)),
                            PersonnelName = GetCellValue<string>(row, columns, nameof(his_importdata.PersonnelName)),
                            Original = GetCellValue<string>(row, columns, nameof(his_importdata.Original)),
                            Value = GetCellValue<decimal>(row, columns, nameof(his_importdata.Value)),
                        };
                        data.Add(importdata);
                    }
                    if (data == null || !data.Any(t => t.Year != 0 && t.Month != 0)) return;

                    data.ForEach(t =>
                    {
                        t.HospitalId = hospitalId;
                        t.AllotId = allots?.FirstOrDefault(w => w.Year == t.Year && t.Month == w.Month)?.ID;
                        t.SourceType = sourceType;
                        t.Category = sheetName;
                    });

                    var years = data.Select(t => t.Year).Distinct();
                    var months = data.Select(t => t.Month).Distinct();

                    hisimportdataRepository.DeleteFromQuery(t => t.HospitalId == hospitalId && years.Contains(t.Year) && months.Contains(t.Month) && t.Category == sheetName);
                    hisimportdataRepository.BulkInsert(data.Where(t => t.Year != 0 && t.Month != 0));
                }
            }
            catch (Exception ex)
            {
                logger.LogError(ex.ToString());
            }
        }

        private List<string> GetColumns(ISheet sheet, string sheetName, out string sourceType)
        {
            sourceType = "";
            foreach (var item in sourceTypes)
            {
                if (item.Value.Contains(sheetName))
                {
                    sourceType = item.Key;
                    break;
                }
            }

            if (string.IsNullOrEmpty(sourceType)) return new List<string>(); ;

            var columnRow = sheet.GetOrCreate(0);
            return columnRow.GetCellValues();
        }

        private T GetCellValue<T>(IRow row, List<string> columns, string key)
        {
            var value = hisImportdataDict[key];
            if (columns.Contains(value) && row.GetCell(columns.IndexOf(value)) != null)
                return row.GetCell(columns.IndexOf(value)).GetCellValue<T>();

            return default;
        }

        private readonly Dictionary<string, string[]> sourceTypes = new Dictionary<string, string[]>
        {
            { "收入", new string[] { "门诊收入", "住院收入" } },
            { "医生工作量", new string[] { "门诊人次", "出院人次", "手术概况", "住院天数", "实际开放总床日数" } },
            { "医技工作量", new string[] { "医技工作量" } },
        };

        private readonly Dictionary<string, string> hisImportdataDict = new Dictionary<string, string>
        {
            { nameof(his_importdata.Year), "年份" },
            { nameof(his_importdata.Month), "月份" },
            { nameof(his_importdata.AccountingUnit), "核算单元" },
            { nameof(his_importdata.Department), "科室" },
            { nameof(his_importdata.PersonnelNumber), "工号" },
            { nameof(his_importdata.PersonnelName), "医生姓名" },
            { nameof(his_importdata.Original), "类别" },
            { nameof(his_importdata.Value), "值" },
        };

        #endregion ImportFile && SaveData
    }
}
