using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json.Linq;
using NPOI.HSSF.UserModel;
using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;
using System.Data;
using System.Dynamic;

namespace WCS.WebApi.Helper
{
    public class ExportExcelHelper
    {
        /// <summary>
        /// </summary>
        /// <param name="sheetName">文件名</param>
        /// <param name="columns">标题行</param>
        /// <param name="results">数据源</param>
        /// <returns></returns>
        public static ActionResult ExportExcel(string sheetName, List<ExportableColumn> columns, IList<dynamic> results)
        {
            var isXlsx = false;
            IWorkbook workbook = new HSSFWorkbook();
            if (results.Count + 1 > 65536)// 包括一行标题行 .xls文件最大行数
            {
                workbook = new XSSFWorkbook();
                isXlsx = true;
            }
            workbook.CreateSheet(sheetName);

            var rowIndex = 0;
            AddHeader(workbook, rowIndex, columns);

            rowIndex++;
            workbook = AddData(workbook, rowIndex, columns, results);

            // Save the Excel spreadsheet to a MemoryStream and return it to the client
            using (var exportData = new MemoryStream())
            {
                workbook.Write(exportData);
                var saveAsFileName = string.Format("{0}-{1:d}.{2}", sheetName, DateTime.Now, (isXlsx ? "xlsx" : "xls")).Replace("/", "-");
                var contentType = isXlsx ? "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" : "application/vnd.ms-excel";
                return new FileContentResult(exportData.ToArray(), contentType) { FileDownloadName = saveAsFileName };
            }
        }

        public static ActionResult Export<T>(string sheetName, IList<ExportableColumn> columns, IEnumerable<T> results)
        {
            var isXlsx = false;
            IWorkbook workbook = new HSSFWorkbook();
            if (results.Count() + 1 > 65536)// 包括一行标题行 .xls文件最大行数
            {
                workbook = new XSSFWorkbook();
                isXlsx = true;
            }
            workbook.CreateSheet(sheetName);
            var rowIndex = 0;

            AddHeader(workbook, rowIndex, columns);

            rowIndex++;
            workbook = AddData(workbook, rowIndex, columns, results);

            // Save the Excel spreadsheet to a MemoryStream and return it to the client
            using (var exportData = new MemoryStream())
            {
                workbook.Write(exportData);
                var saveAsFileName = string.Format("{0}-{1:d}.{2}", sheetName, DateTime.Now, (isXlsx ? "xlsx" : "xls")).Replace("/", "-");
                var contentType = isXlsx ? "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" : "application/vnd.ms-excel";
                return new FileContentResult(exportData.ToArray(), contentType) { FileDownloadName = saveAsFileName };
            }
        }

        public static ActionResult Export<T>(
            string sheetName,
            string firstData,
            IList<ExportableColumn> columns,
            IEnumerable<T> results
        )
        {
            IWorkbook workbook = new HSSFWorkbook();
            var sheet = workbook.CreateSheet(sheetName);
            var rowIndex = 0;

            // first row data
            var firstRow = sheet.CreateRow(rowIndex);
            firstRow.CreateCell(0).SetCellValue(firstData);
            rowIndex++;

            AddHeader(workbook, rowIndex, columns);

            rowIndex++;
            workbook = AddData(workbook, rowIndex, columns, results);

            // Save the Excel spreadsheet to a MemoryStream and return it to the client
            using (var exportData = new MemoryStream())
            {
                workbook.Write(exportData);
                string saveAsFileName = string.Format("{0}-{1:d}.xls", sheetName, DateTime.Now).Replace("/", "-");
                return new FileContentResult(exportData.ToArray(), "application/vnd.ms-excel") { FileDownloadName = saveAsFileName };
            }
        }

        #region 数据操作
        public static IWorkbook AddData(IWorkbook workbook, int rowIndex, List<ExportableColumn> columns, IList<dynamic> results)
        {
            const int MaxRowCount = 1048575; //.xlsx文件最大行数1048576
            var sheet = workbook.GetSheetAt(0);

            // Add data rows
            foreach (var viewModel in results)
            {
                var row = sheet.CreateRow(rowIndex);
                if (rowIndex >= MaxRowCount)
                {
                    row.CreateCell(0).SetCellValue("数据行数已超出Excel文件支持的最大行数！");
                    break;
                }

                for (var index = 0; index < columns.Count; index++)
                {
                    var propertyName = columns[index].Name.Trim();
                    var cell = row.CreateCell(index);
                    var propertyDictionary = (IDictionary<string, object>)viewModel;
                    if (propertyDictionary.ContainsKey(propertyName))
                    {
                        var value = propertyDictionary[propertyName];
                        cell.SetCellValue(value == null ? "" : value.ToString());
                    }
                }

                rowIndex++;
            }

            return workbook;
        }

        public static IWorkbook AddData<T>(IWorkbook workbook, int rowIndex, IList<ExportableColumn> columns, IEnumerable<T> results)
        {
            const int MaxRowCount = 1048575; //.xlsx文件最大行数1048576
            var sheet = workbook.GetSheetAt(0);

            // Add data rows
            foreach (var viewModel in results)
            {
                var row = sheet.CreateRow(rowIndex);
                if (rowIndex >= MaxRowCount)
                {
                    row.CreateCell(0).SetCellValue("数据行数已超出Excel文件支持的最大行数！");
                    break;
                }

                for (int index = 0; index < columns.Count; index++)
                {
                    var cell = row.CreateCell(index);
                    var dr = viewModel as DataRow;
                    var value = (dr != null) ? dr[columns[index].Name]
                        : viewModel.GetType().GetProperty(columns[index].Name)?.GetValue(viewModel, null);

                    if (value == null)
                    {
                        var item = viewModel as ExpandoObject;
                        if (item != null)
                        {
                            if (item.Any(w => w.Key == columns[index].Name))
                            {
                                var keyValue = item.FirstOrDefault(w => w.Key == columns[index].Name);
                                value = keyValue.Value;
                            }
                        }
                    }
                    if (value == null)
                    {
                        var item = viewModel as JObject;
                        if (item != null)
                        {
                            value = item[columns[index].Name];
                        }
                    }

                    cell.SetCellValue(value == null ? "" : value.ToString());
                }

                rowIndex++;
            }

            return workbook;
        }

        public static void AddHeader(IWorkbook workbook, int rowIndex, IList<ExportableColumn> columns)
        {
            var sheet = workbook.GetSheetAt(0);
            var format = workbook.CreateDataFormat();

            // Add header labels
            var headerRow = sheet.CreateRow(rowIndex);
            for (int index = 0; index < columns.Count; index++)
            {
                headerRow.CreateCell(index).SetCellValue(columns[index].Caption);
                if (!string.IsNullOrEmpty(columns[index].Format))
                {
                    var columnStyle = workbook.CreateCellStyle();
                    columnStyle.DataFormat = format.GetFormat(columns[index].Format);
                    sheet.SetDefaultColumnStyle(index, columnStyle);
                }
                if (columns[index].ColumnWidth.HasValue && columns[index].ColumnWidth.Value > 0)
                {
                    var columnWidth = columns[index].ColumnWidth.Value;
                    sheet.SetColumnWidth(index, columnWidth * 256 / 7);
                }
            }
        }
        #endregion
    }

    public class ExportableColumn
    {
        public ExportableColumn(string caption, string name) : this(caption, name, null)
        {
        }

        public ExportableColumn(string caption, string name, string format)
        {
            Format = format;
            Caption = caption;
            Name = name;
        }

        public ExportableColumn(string caption, string name, string format, int? width)
            : this(caption, name, format)
        {
            ColumnWidth = width;
        }

        public string Name { get; }
        public string Caption { get; }
        public string Format { get; }

        /// <summary>
        ///     单位：像素，为空时使用默认值
        /// </summary>
        public int? ColumnWidth { get; private set; }

        public ExportableColumn Width(int? width)
        {
            ColumnWidth = width;
            return this;
        }
    }
}
