using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using NPOI.HSSF.UserModel;
using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;
using OfficeOpenXml;
using OfficeOpenXml.Style;
using Performance.DtoModels;
using Performance.EntityModels;
using Performance.Infrastructure;
using Performance.Repository;
using Performance.Services.ExtractExcelService;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;

namespace Performance.Services
{
    public class DownloadService : IAutoInjection
    {
        private readonly ILogger<DownloadService> logger;
        private readonly PerforPerallotRepository perallotRepository;
        private readonly PerforHospitalRepository perforHospital;
        private readonly PerforCofaliasRepository perforCofalias;
        private readonly ConfigService configService;
        private readonly ComputeService _computeService;
        private readonly IWebHostEnvironment evn;

        public DownloadService(ILogger<DownloadService> logger,
            PerforPerallotRepository perallotRepository,
            PerforHospitalRepository perforHospital,
            PerforCofaliasRepository perforCofalias,
            ConfigService configService,
            ComputeService computeService,
            IWebHostEnvironment evn)
        {
            this.logger = logger;
            this.perallotRepository = perallotRepository;
            this.perforHospital = perforHospital;
            this.perforCofalias = perforCofalias;
            this.configService = configService;
            _computeService = computeService;
            this.evn = evn;
        }




        #region 财务、全院绩效发放下载


        /// <summary>
        /// 财务、全院绩效发放下载
        /// </summary>
        /// <param name="allotId"></param>
        /// <param name="allData"></param>
        /// <param name="isAll"></param>
        /// <returns></returns>
        public string AllComputerReport(int allotId, List<ComputeResponse> allData, bool isAll, string name)
        {
            var allot = perallotRepository.GetEntity(t => t.ID == allotId);
            var hospital = perforHospital.GetEntity(t => t.ID == allot.HospitalId);
            var alias = perforCofalias.GetEntities(t => t.HospitalId == hospital.ID && t.States == 1);
            var headShow = new List<string> { "银行卡号", "正式/临聘", "职务", "职称" };

            var headList = AllCompute;
            if (!isAll)
            {
                headList = Person;
                alias = alias?.Where(t => t.Route == "/result/wholeHospital")?.ToList();
                if (alias != null)
                    headShow = headShow.Except(alias.Select(t => t.OriginalName)).ToList();

            }
            else
            {
                alias = alias?.Where(t => t.Route == "/result/compute")?.ToList();
                if (alias != null)
                    headShow = headShow.Except(alias.Select(t => t.OriginalName)).ToList();

            }

            var dpath = Path.Combine(evn.ContentRootPath, "Files", "PerformanceRelease", $"{allot.HospitalId}");
            FileHelper.CreateDirectory(dpath);

            string fileName = $"{hospital.HosName}-{name}-{DateTime.Now:yyyy年MM月dd日}";
            string filepath = Path.Combine(dpath, fileName);
            FileStream stream = new FileStream(filepath, FileMode.Create);
            try
            {
                XSSFWorkbook workbook = new XSSFWorkbook();
                //设置单元格样式
                ICellStyle style = workbook.CreateCellStyle();
                style.Alignment = HorizontalAlignment.Center;
                style.BorderBottom = BorderStyle.Thin;
                style.BorderRight = BorderStyle.Thin;

                ISheet sheet = workbook.CreateSheet($"{name}");
                sheet.ForceFormulaRecalculation = true;
                ICellStyle cellStyle = workbook.CreateCellStyle();
                cellStyle.DataFormat = HSSFDataFormat.GetBuiltinFormat("0.00");

                IRow row1 = sheet.CreateRow(0);
                row1.CreateCell(0).SetCellValue("序号");
                row1.GetCell(0).CellStyle = style;

                int cellIndex = 1;
                foreach (var item in headList)
                {
                    if (headShow.Contains(item.Item1))
                        continue;

                    row1.CreateCell(cellIndex).SetCellValue(item.Item1);
                    row1.GetCell(cellIndex).CellStyle = style;
                    sheet.SetColumnWidth(cellIndex, 14 * 256);
                    cellIndex++;
                }

                int startIndex = 1;
                foreach (var item in allData)
                {
                    var row = sheet.CreateRow(startIndex);
                    cellIndex = 1;
                    var serial = row.CreateCell(0);
                    serial.SetCellOValue(startIndex);
                    serial.CellStyle = style;
                    foreach (var field in headList)
                    {
                        if (headShow.Contains(field.Item1))
                            continue;

                        var cell = row.CreateCell(cellIndex);
                        cell.SetCellOValue(field.Item2?.Invoke(item));
                        cell.CellStyle = style;
                        cellIndex++;
                    }
                    startIndex++;
                }
                //合计
                cellIndex = 1;
                var totalRow = sheet.CreateRow(startIndex);
                var totalSerial = totalRow.CreateCell(0);
                totalSerial.SetCellOValue("合计");
                totalSerial.CellStyle = style;
                foreach (var value in headList)
                {
                    if (headShow.Contains(value.Item1))
                        continue;

                    var cell = totalRow.CreateCell(cellIndex);
                    cell.SetCellOValue(value.Item3?.Invoke(allData));
                    cell.CellStyle = style;
                    cellIndex++;
                }

                workbook.Write(stream);
            }
            catch (Exception ex)
            {
                Console.WriteLine("下载异常" + ex);
            }
            finally
            {
                stream.Close();
            }
            return filepath;
        }

        /// <summary>
        /// 财务、全院绩效发放下载
        /// </summary>
        /// <param name="allotId"></param>
        /// <param name="allData"></param>
        /// <param name="isAll"></param>
        /// <returns></returns>
        public string AllComputerViewReport(int allotId, List<dynamic> dynamics, string route, string name)
        {
            var allot = perallotRepository.GetEntity(t => t.ID == allotId);
            var hospital = perforHospital.GetEntity(t => t.ID == allot.HospitalId);
            var title = $"{allot.Year}年{allot.Month}月{hospital.HosName}医院 --- {name}";
            return AllComputerDown(hospital, dynamics, route, title, name);
        }
        /// <summary>
        /// 财务、全院绩效发放下载（时间段）
        /// </summary>
        /// <param name="allotId"></param>
        /// <param name="allData"></param>
        /// <param name="isAll"></param>
        /// <returns></returns>
        public string AllComputerViewReportByDate(int hospitalId, List<dynamic> dynamics, string route, string name, DateTime beginTime, DateTime endTime,params string[] headlist)
        {
            var hospital = perforHospital.GetEntity(t => t.ID == hospitalId);

            var title = (beginTime.AddMonths(1).Date == endTime.Date)
                ? $"{beginTime.ToString("yyyy年MM月")}{hospital.HosName}医院 --- {name}"
                : $"{beginTime.ToString("yyyy年MM月")}至{endTime.AddMonths(-1).ToString("yyyy年MM月")}{hospital.HosName}医院 --- {name}";
            return AllComputerDown(hospital, dynamics, route, title, name, headlist);
        }
        public string AllComputerDown(sys_hospital hospital, List<dynamic> dynamics, string route, string title, string name, params string[] headlist)
        {
            var ser = JsonHelper.Serialize(dynamics);
            var dict = JsonHelper.Deserialize<List<Dictionary<string, object>>>(ser);

            var data = new List<Dictionary<string, object>>();
            foreach (var obj in dict)
            {
                Dictionary<string, object> nobj = new Dictionary<string, object>();

                foreach (var item in obj)
                {
                    nobj[item.Key.ToLower()] = item.Value;
                }
                data.Add(nobj);
            }
            
            var headList = _computeService.CustomColumnHeaders(hospital.ID, route, headlist).Where(w => w.States == 1).ToList();
            
            var dpath = Path.Combine(evn.ContentRootPath, "Files", "PerformanceRelease", $"{hospital.ID}");
            FileHelper.CreateDirectory(dpath);
            string filepath = Path.Combine(dpath, $"{hospital.HosName}-{name}-{DateTime.Now:yyyy年MM月dd日}");
            FileHelper.DeleteFile(filepath);

            string[] notSum = new string[] { "year", "month" };
            using (FileStream fs = new FileStream(filepath, FileMode.OpenOrCreate))
            using (ExcelPackage package = new ExcelPackage(fs))
            {
                var worksheet = package.Workbook.Worksheets.Add(name);

                if (dynamics != null && dynamics.Count() > 0)
                {
                    worksheet.SetValue(1, 1, title);

                    var headers = ((IDictionary<string, object>)dynamics.ElementAt(0)).Keys;
                    for (int col = 0; col < headList.Count; col++)
                    {
                        worksheet.SetValue(2, col + 1, headList[col].Alias);
                    }
                    for (int col = 0; col < headList.Count; col++)
                    {
                        for (int row = 0; row < data.Count(); row++)
                        {
                            var temp = data.ElementAt(row);
                            var low = temp.Keys.ToString().ToLower();
                            var value = temp[headList[col].Name.ToLower()];

                            worksheet.Cells[row + 3, col + 1].Value = value;
                        }
                        if (col == 0)
                            worksheet.SetValue(dynamics.Count() + 3, col + 1, "合计");
                        else if (!notSum.Contains(headList[col].Name.ToLower()))
                            worksheet.Cells[dynamics.Count() + 3, col + 1].Formula = string.Format("SUM({0})", new ExcelAddress(3, col + 1, dynamics.Count() + 2, col + 1).Address);
                    }

                    #region 样式设置
                    for (int row = worksheet.Dimension.Start.Row; row <= worksheet.Dimension.End.Row; row++)
                    {
                        worksheet.Row(row).Height = 20;
                        for (int col = worksheet.Dimension.Start.Column; col <= worksheet.Dimension.End.Column; col++)
                        {
                            if (headList.Count < col && !notSum.Contains(headList[col - 1].Name.ToLower()))
                                worksheet.Cells[row, col].Style.Numberformat.Format = "#,##0.00";

                            worksheet.Cells[row, col].Style.Border.BorderAround(ExcelBorderStyle.Thin);
                            worksheet.Cells[row, col].Style.VerticalAlignment = ExcelVerticalAlignment.Center;
                        }
                    }
                    worksheet.Cells[1, 1, 1, headList.Count].Merge = true;
                    worksheet.Cells[1, 1, 1, headList.Count].Style.VerticalAlignment = ExcelVerticalAlignment.Center;
                    worksheet.Cells[1, 1, 1, headList.Count].Style.HorizontalAlignment = ExcelHorizontalAlignment.Center;
                    worksheet.Cells[1, 1, 1, headList.Count].Style.Font.Bold = true;
                    worksheet.Cells[1, 1, 1, headList.Count].Style.Font.Size = 16;
                    worksheet.Row(1).Height = 24;
                    worksheet.Cells[2, 1, 2, headList.Count].Style.Font.Bold = true;
                    worksheet.View.FreezePanes(3, 1);
                    worksheet.Cells.AutoFitColumns();
                    for (int col = worksheet.Dimension.Start.Column; col <= worksheet.Dimension.End.Column; col++)
                    {
                        worksheet.Column(col).Width = worksheet.Column(col).Width > 20 ? 20 : worksheet.Column(col).Width;
                    }
                    #endregion
                }
                package.Save();
            }
            return filepath;
        }


        public static List<(string, Func<ComputeResponse, object>, Func<List<ComputeResponse>, decimal>)> AllCompute { get; } = new List<(string, Func<ComputeResponse, object>, Func<List<ComputeResponse>, decimal>)>
        {
            ("来源",t=>t.Source,null),
            ("科室类别",t=>t.UnitType,null),
            ("核算单元",t=>t.AccountingUnit,null),
            ("员工号",t=>t.JobNumber,null),
            ("人员姓名",t=>t.EmployeeName,null),
            ("职务",t=>t.JobTitle,null),
            ("批次",t=>t.Batch,null),
            ("调节后业绩绩效",t=>t.PerforSumFee,(t) => Math.Round(t.Sum(item => item.PerforSumFee ?? 0), 2, MidpointRounding.AwayFromZero)),
            ("调节后实发管理绩效",t=>t.PerforManagementFee,(t) => Math.Round(t.Sum(item => item.PerforManagementFee ?? 0), 2, MidpointRounding.AwayFromZero)),
            ("调节后其他绩效",t=>t.AdjustLaterOtherFee,(t) => Math.Round(t.Sum(item => item.AdjustLaterOtherFee ?? 0), 2, MidpointRounding.AwayFromZero)),
            ("夜班费",t=>t.NightWorkPerfor,(t) => Math.Round(t.Sum(item => item.NightWorkPerfor ?? 0), 2, MidpointRounding.AwayFromZero)),
            ("医院其他绩效",t=>t.OthePerfor,(t) => Math.Round(t.Sum(item => item.OthePerfor ?? 0), 2, MidpointRounding.AwayFromZero)),
            ("不公示其他绩效",t=>t.HideOtherPerfor,(t) => Math.Round(t.Sum(item => item.HideOtherPerfor ?? 0), 2, MidpointRounding.AwayFromZero)),
            ("应发小计",t=>t.ShouldGiveFee,(t) => Math.Round(t.Sum(item => item.ShouldGiveFee ?? 0), 2, MidpointRounding.AwayFromZero)),
            ("预留绩效",t=>t.ReservedRatioFee,(t) => Math.Round(t.Sum(item => item.ReservedRatioFee ?? 0), 2, MidpointRounding.AwayFromZero)),
            ("实发绩效",t=>t.RealGiveFee,(t) => Math.Round(t.Sum(item => item.RealGiveFee ?? 0), 2, MidpointRounding.AwayFromZero)),
            ("银行卡号",t=>t.BankCard,null),
            ("正式/临聘",t=>t.JobCategory,null),
            ("职务",t=>t.Duty,null),
            ("职称",t=>t.TitlePosition,null),
        };
        public static List<(string, Func<ComputeResponse, object>, Func<List<ComputeResponse>, decimal>)> Person { get; } = new List<(string, Func<ComputeResponse, object>, Func<List<ComputeResponse>, decimal>)>
        {
            ("科室类别",t=>t.UnitType,null),
            ("核算单元",t=>t.AccountingUnit,null),
            ("员工号",t=>t.JobNumber,null),
            ("人员姓名",t=>t.EmployeeName,null),
            ("调节后业绩绩效",t=>t.PerforSumFee,(t) => Math.Round(t.Sum(item => item.PerforSumFee ?? 0), 2, MidpointRounding.AwayFromZero)),
            ("调节后实发管理绩效",t=>t.PerforManagementFee,(t) => Math.Round(t.Sum(item => item.PerforManagementFee ?? 0), 2, MidpointRounding.AwayFromZero)),
            ("调节后其他绩效",t=>t.AdjustLaterOtherFee,(t) => Math.Round(t.Sum(item => item.AdjustLaterOtherFee ?? 0), 2, MidpointRounding.AwayFromZero)),
            ("夜班费",t=>t.NightWorkPerfor,(t) => Math.Round(t.Sum(item => item.NightWorkPerfor ?? 0), 2, MidpointRounding.AwayFromZero)),
            ("医院其他绩效",t=>t.OthePerfor,(t) => Math.Round(t.Sum(item => item.OthePerfor ?? 0), 2, MidpointRounding.AwayFromZero)),
            ("不公示其他绩效",t=>t.HideOtherPerfor,(t) => Math.Round(t.Sum(item => item.HideOtherPerfor ?? 0), 2, MidpointRounding.AwayFromZero)),
            ("应发小计",t=>t.ShouldGiveFee,(t) => Math.Round(t.Sum(item => item.ShouldGiveFee ?? 0), 2, MidpointRounding.AwayFromZero)),
            ("预留绩效",t=>t.ReservedRatioFee,(t) => Math.Round(t.Sum(item => item.ReservedRatioFee ?? 0), 2, MidpointRounding.AwayFromZero)),
            ("实发绩效",t=>t.RealGiveFee,(t) => Math.Round(t.Sum(item => item.RealGiveFee ?? 0), 2, MidpointRounding.AwayFromZero))
        };


        #endregion

        #region 全院绩效核算


        /// <summary>
        /// 全院绩效核算
        /// </summary>
        /// <param name="allotId"></param>
        /// <param name="name"></param>
        /// <param name="deptData"></param>
        /// <returns></returns>
        public string DeptReport(int allotId, List<DeptResponse> deptData)
        {
            var allot = perallotRepository.GetEntity(t => t.ID == allotId);
            var hospital = perforHospital.GetEntity(t => t.ID == allot.HospitalId);

            var dpath = Path.Combine(evn.ContentRootPath, "Files", "PerformanceRelease", $"{allot.HospitalId}");
            FileHelper.CreateDirectory(dpath);

            string fileName = $"{hospital.HosName}-全院核算绩效发放-{DateTime.Now:yyyy年MM月dd日}";
            string filepath = Path.Combine(dpath, fileName);
            FileStream stream = new FileStream(filepath, FileMode.Create);
            try
            {
                XSSFWorkbook workbook = new XSSFWorkbook();
                //设置单元格样式
                ICellStyle style = workbook.CreateCellStyle();
                style.Alignment = HorizontalAlignment.Center;
                style.BorderBottom = BorderStyle.Thin;
                style.BorderRight = BorderStyle.Thin;

                ISheet sheet = workbook.CreateSheet("全院核算绩效发放");
                sheet.ForceFormulaRecalculation = true;
                ICellStyle cellStyle = workbook.CreateCellStyle();
                cellStyle.DataFormat = HSSFDataFormat.GetBuiltinFormat("0.00");

                IRow row1 = sheet.CreateRow(0);
                int cellIndex = 0;
                foreach (var item in HosData.Select(t => t.Item1))
                {
                    row1.CreateCell(cellIndex).SetCellValue(item);
                    row1.GetCell(cellIndex).CellStyle = style;
                    sheet.SetColumnWidth(cellIndex, 14 * 256);
                    cellIndex++;
                }

                int startIndex = 1;
                foreach (var item in deptData)
                {
                    var row = sheet.CreateRow(startIndex);
                    cellIndex = 0;
                    foreach (var field in HosData.Select(t => t.Item2))
                    {
                        var cell = row.CreateCell(cellIndex);
                        cell.SetCellOValue(field?.Invoke(item));
                        cell.CellStyle = style;
                        cellIndex++;
                    }
                    startIndex++;
                }

                //合计
                cellIndex = 0;
                var totalRow = sheet.CreateRow(startIndex);

                foreach (var value in HosData.Select(t => t.Item3))
                {
                    var cell = totalRow.CreateCell(cellIndex);
                    if (cellIndex == 0)
                        cell.SetCellOValue("合计");
                    else
                        cell.SetCellOValue(value?.Invoke(deptData));
                    cell.CellStyle = style;
                    cellIndex++;
                }


                workbook.Write(stream);
            }
            catch (Exception ex)
            {
                Console.WriteLine("下载异常" + ex);
            }
            finally
            {
                stream.Close();
            }
            return filepath;
        }

        public static List<(string, Func<DeptResponse, object>, Func<List<DeptResponse>, decimal>)> HosData { get; } = new List<(string, Func<DeptResponse, object>, Func<List<DeptResponse>, decimal>)>
        {
            ("核算群体",t=>t.UnitName,null),
            ("核算单元",t=>t.AccountingUnit,null),
            ("业绩绩效",t=>t.PerforFee,(t) => Math.Round(t.Sum(item => item.PerforFee ?? 0), 2, MidpointRounding.AwayFromZero)),
            ("工作量绩效",t=>t.WorkloadFee,(t) => Math.Round(t.Sum(item => item.WorkloadFee ?? 0), 2, MidpointRounding.AwayFromZero)),
            ("考核前其他绩效",t=>t.AssessBeforeOtherFee,(t) => Math.Round(t.Sum(item => item.AssessBeforeOtherFee ?? 0), 2, MidpointRounding.AwayFromZero)),
            ("考核前绩效合计",t=>t.PerforTotal,(t) => Math.Round(t.Sum(item => item.PerforTotal ?? 0), 2, MidpointRounding.AwayFromZero)),
            ("科室考核得分",t=>t.ScoringAverage,(t) => Math.Round(t.Sum(item => item.ScoringAverage ?? 0), 2, MidpointRounding.AwayFromZero)),
            ("药占比奖罚",t=>t.MedicineExtra,(t) => Math.Round(t.Sum(item => item.MedicineExtra ?? 0), 2, MidpointRounding.AwayFromZero)),
            ("材料占比奖罚",t=>t.MaterialsExtra,(t) => Math.Round(t.Sum(item => item.MaterialsExtra ?? 0), 2, MidpointRounding.AwayFromZero)),
            ("医院奖罚",t=>t.Extra,(t) => Math.Round(t.Sum(item => item.Extra ?? 0), 2, MidpointRounding.AwayFromZero)),
            ("考核后其他绩效",t=>t.AssessLaterOtherFee,(t) => Math.Round(t.Sum(item => item.AssessLaterOtherFee ?? 0), 2, MidpointRounding.AwayFromZero)),
            ("考核后绩效",t=>t.AssessLaterPerforTotal,(t) => Math.Round(t.Sum(item => item.AssessLaterPerforTotal ?? 0), 2, MidpointRounding.AwayFromZero)),
            ("调节系数",t=>t.AdjustFactor,(t) => Math.Round(t.Sum(item => item.AdjustFactor ?? 0), 2, MidpointRounding.AwayFromZero)),
            ("调节后其他绩效",t=>t.AdjustLaterOtherFee,(t) => Math.Round(t.Sum(item => item.AdjustLaterOtherFee ?? 0), 2, MidpointRounding.AwayFromZero)),
            ("科主任实发管理绩效",t=>t.AssessLaterManagementFee,(t) => Math.Round(t.Sum(item => item.AssessLaterManagementFee ?? 0), 2, MidpointRounding.AwayFromZero)),
            ("医院其他绩效",t=>t.AprPerforAmount,(t) => Math.Round(t.Sum(item => item.AprPerforAmount ?? 0), 2, MidpointRounding.AwayFromZero)),
            ("不公示其他绩效",t=>t.HideAprOtherPerforAmount,(t) => Math.Round(t.Sum(item => item.HideAprOtherPerforAmount ?? 0), 2, MidpointRounding.AwayFromZero)),
            ("实发绩效",t=>t.RealGiveFee,(t) => Math.Round(t.Sum(item => item.RealGiveFee ?? 0), 2, MidpointRounding.AwayFromZero)),
        };

        #endregion
    }
}
