using NPOI.SS.UserModel;
using Performance.DtoModels;
using Performance.EntityModels;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Performance.Services.ExtractExcelService
{
    public class WriteDataHelper
    {
        public enum UnitType
        {
            护理组,
            医生组,
            医技组
        }

        public static void WriteSheetHeader(ISheet sheet, PerSheetPoint point, SheetType sheetType, ExcelStyle style, List<ExcelHeader> headers)
        {
            if (headers == null || !headers.Any()) return;

            var columns = headers.Select(t => t).ToList();

            // 收入支出系数
            var nurseFactor = sheet.GetOrCreate(point.AccountingUnit.FirstOrDefault(t => t.UnitType == UnitType.护理组.ToString())?.FactorRow.Value ?? 0);
            var doctorFactor = sheet.GetOrCreate(point.AccountingUnit.FirstOrDefault(t => t.UnitType == UnitType.医生组.ToString())?.FactorRow.Value ?? 0);
            var technicianFactor = sheet.GetOrCreate(point.AccountingUnit.FirstOrDefault(t => t.UnitType == UnitType.医技组.ToString())?.FactorRow.Value ?? 0);
            // 费用类型
            var columnHeader = sheet.GetOrCreate(point.HeaderFirstRowNum.Value);
            // 工作量系数
            var workloadFactor = sheet.GetOrCreate(point.HeaderFirstRowNum.Value + 1);

            // 去除excel中已存在的列
            int headerFirstCellNum = point.DataFirstCellNum.Value;
            if (columnHeader.LastCellNum > point.DataFirstCellNum)
            {
                for (int index = headerFirstCellNum; index < columnHeader.LastCellNum; index++)
                {
                    var column = columnHeader.GetCell(index).GetDecodeEscapes();
                    if (string.IsNullOrEmpty(column)) continue;

                    columns.RemoveAll(t => t.ColumnName == column);
                    if (index > headerFirstCellNum)
                        headerFirstCellNum = index + 1;
                }
            }

            if (columns == null || !columns.Any()) return;
            var factorStyle = sheetType == SheetType.Workload
                ? style.SetBgkColorAndFormat(style.GetCellStyle(), StyleType.系数, CellFormat.数字)
                : style.SetBgkColorAndFormat(style.GetCellStyle(), StyleType.系数, CellFormat.百分比);
            var columnStyle = style.SetBgkColorAndFormat(style.GetCellStyle(), StyleType.列头);

            // 补充excel中不存在的列
            foreach (var item in columns)
            {
                var columnCell = columnHeader.GetOrCreate(headerFirstCellNum);
                columnCell.SetCellValue(item.ColumnName);
                columnCell.CellStyle = columnStyle;

                if (sheetType == SheetType.Workload)
                {
                    var workloadCell = workloadFactor.GetOrCreate(headerFirstCellNum);
                    workloadCell.SetCellValue<decimal>(item.WorkloadFactor);
                    workloadCell.CellStyle = factorStyle;
                }
                else
                {
                    var doctorCell = doctorFactor.GetOrCreate(headerFirstCellNum);
                    doctorCell.SetCellValue<decimal>(item.DoctorFactor);
                    doctorCell.CellStyle = factorStyle;

                    var nurseCell = nurseFactor.GetOrCreate(headerFirstCellNum);
                    nurseCell.SetCellValue<decimal>(item.NurseFactor);
                    nurseCell.CellStyle = factorStyle;

                    var technicianCell = technicianFactor.GetOrCreate(headerFirstCellNum);
                    technicianCell.SetCellValue<decimal>(item.TechnicianFactor);
                    technicianCell.CellStyle = factorStyle;
                }

                headerFirstCellNum++;
            }
        }

        #region ExtractData

        public static void WriteSheetData(ISheet sheet, PerSheetPoint point, SheetType sheetType, ExcelStyle style, List<string> headers, List<ExtractTransDto> data)
        {
            var columnHeader = sheet.GetOrCreate(point.HeaderFirstRowNum.Value);
            int dataFirstRowNum = point.DataFirstRowNum.Value;

            WriteSheetDataExistent(sheet, columnHeader, point, sheetType, style, headers, data, ref dataFirstRowNum);

            if (data == null || !data.Any(t => !string.IsNullOrEmpty(t.Department))) return;

            WriteSheetDataNonexistent(sheet, columnHeader, point, sheetType, style, headers, data, dataFirstRowNum);
        }

        private static void WriteSheetDataExistent(ISheet sheet, IRow columnHeader, PerSheetPoint point, SheetType sheetType, ExcelStyle style,
            List<string> headers, List<ExtractTransDto> data, ref int dataFirstRowNum)
        {
            if (sheet.LastRowNum <= dataFirstRowNum) return;

            var cellStyle = style.SetBgkColorAndFormat(style.GetCellStyle(), StyleType.数据);

            int dataFirstCellNum = point.DataFirstCellNum.Value;

            for (int rowIndex = dataFirstRowNum; rowIndex < sheet.LastRowNum + 1; rowIndex++)
            {
                var row = sheet.GetRow(rowIndex);
                if (row == null) continue;

                string department = row.GetOrCreate(dataFirstCellNum - 1).GetDecodeEscapes();
                if (string.IsNullOrEmpty(department)) continue;

                var deptData = data.Where(t => t.Department == department);
                if (deptData == null || !deptData.Any()) continue;

                for (int cellIndex = dataFirstCellNum; cellIndex < columnHeader.LastCellNum; cellIndex++)
                {
                    var column = columnHeader.GetOrCreate(cellIndex).GetDecodeEscapes();
                    var cell = row.GetOrCreate(cellIndex);

                    var value = deptData.FirstOrDefault(t => t.Category == column)?.Value;
                    var notWrite = !value.HasValue && !string.IsNullOrEmpty(cell.GetDecodeEscapes());

                    if (sheetType == SheetType.Income)
                    {
                        cell.SetCellValue<decimal>(value);
                        cell.CellStyle = cellStyle;
                    }
                    else if (cell.CellType != CellType.Formula && !notWrite)
                    {
                        cell.SetCellValue<decimal>(value);
                        if (headers != null && headers.Contains(column))
                        {
                            cell.CellStyle = cellStyle;
                        }
                    }
                }

                data.RemoveAll(t => t.Department == department);
                if (rowIndex > dataFirstRowNum) dataFirstRowNum = rowIndex + 1;
            }
        }

        private static void WriteSheetDataNonexistent(ISheet sheet, IRow columnHeader, PerSheetPoint point, SheetType sheetType, ExcelStyle style,
            List<string> headers, List<ExtractTransDto> data, int dataFirstRowNum)
        {
            var departments = data.Select(s => s.Department).Where(w => !string.IsNullOrEmpty(w)).Distinct().ToList();

            var filed = sheet.SheetName.Contains("住院") ? fieldInpat : fieldOut;
            if (sheet.SheetName.Contains("工作量"))
            {
                filed = sheet.SheetName.Contains("医生") ? fieldDoctor : fieldNurse;
            }

            var deptStyle = style.GetCellStyle();
            var cellStyle = style.SetBgkColorAndFormat(style.GetCellStyle(), StyleType.数据);

            foreach (string department in departments)
            {
                var deptData = data.Where(t => t.Department == department);
                if (deptData == null || !deptData.Any()) continue;

                var row = sheet.GetOrCreate(dataFirstRowNum);
                for (int cellIndex = point.HeaderFirstCellNum.Value; cellIndex < columnHeader.LastCellNum; cellIndex++)
                {
                    var column = columnHeader.GetCell(cellIndex).GetDecodeEscapes();
                    var cell = row.CreateCell(cellIndex);

                    if (filed.ContainsKey(column))
                    {
                        cell.SetCellOValue(filed[column]?.Invoke(deptData.First()));
                        cell.CellStyle = deptStyle;
                    }
                    else if (sheetType == SheetType.Income || (headers != null && headers.Contains(column)))
                    {
                        var value = deptData.FirstOrDefault(t => t.Category == column)?.Value;
                        cell.SetCellValue<decimal>(value);
                        cell.CellStyle = cellStyle;
                    }
                }
                dataFirstRowNum++;
            }
        }

        public static string HasValue(params string[] list)
        {
            if (list == null || !list.Any()) return null;

            return list.FirstOrDefault(t => !string.IsNullOrEmpty(t));
        }

        /// <summary> 住院核算单元 </summary>
        private static readonly Dictionary<string, Func<ExtractTransDto, string>> fieldInpat = new Dictionary<string, Func<ExtractTransDto, string>>
        {
            { "科室名称", (dto) => dto.Department },
            { "核算单元（医生组）", (dto) => new string []{ dto.InpatDoctorAccounting, dto.OutDoctorAccounting }.FirstOrDefault(t => !string.IsNullOrEmpty(t)) },
            { "核算单元（护理组）", (dto) => new string []{ dto.InpatNurseAccounting, dto.OutNurseAccounting }.FirstOrDefault(t => !string.IsNullOrEmpty(t)) },
            { "核算单元（医技组）", (dto) => new string []{ dto.InpatTechnicAccounting, dto.OutTechnicAccounting }.FirstOrDefault(t => !string.IsNullOrEmpty(t)) },
        };

        /// <summary> 门诊核算单元 </summary>
        private static readonly Dictionary<string, Func<ExtractTransDto, string>> fieldOut = new Dictionary<string, Func<ExtractTransDto, string>>
        {
            { "科室名称", (dto) => dto.Department },
            { "核算单元（医生组）", (dto) => new string []{ dto.OutDoctorAccounting, dto.InpatDoctorAccounting }.FirstOrDefault(t => !string.IsNullOrEmpty(t)) },
            { "核算单元（护理组）", (dto) => new string []{ dto.OutNurseAccounting, dto.InpatNurseAccounting }.FirstOrDefault(t => !string.IsNullOrEmpty(t)) },
            { "核算单元（医技组）", (dto) => new string []{ dto.OutTechnicAccounting, dto.InpatTechnicAccounting }.FirstOrDefault(t => !string.IsNullOrEmpty(t)) },
        };

        /// <summary> 医生工作量 </summary>
        private static readonly Dictionary<string, Func<ExtractTransDto, string>> fieldDoctor = new Dictionary<string, Func<ExtractTransDto, string>>
        {
            { "科室名称", (dto) => dto.Department },
            { "核算单元", (dto) =>
                          {
                              var obj = new string []{ dto.OutDoctorAccounting, dto.InpatDoctorAccounting, dto.OutTechnicAccounting, dto.InpatTechnicAccounting }
                                .FirstOrDefault(t => !string.IsNullOrEmpty(t));
                              return obj;
                          }
            },
        };

        /// <summary> 护理工作量 </summary>
        private static readonly Dictionary<string, Func<ExtractTransDto, string>> fieldNurse = new Dictionary<string, Func<ExtractTransDto, string>>
        {
            { "科室名称", (dto) => dto.Department },
            { "核算单元", (dto) => new string []{ dto.OutNurseAccounting, dto.InpatNurseAccounting }.FirstOrDefault(t => !string.IsNullOrEmpty(t)) },
        };

        #endregion

        #region CollectData

        public static void WriteCollectData(ISheet sheet, PerSheetPoint point, SheetType sheetType, ExcelStyle style, List<string> headers, List<collect_data> data)
        {
            var columnHeader = sheet.GetOrCreate(point.HeaderFirstRowNum.Value);
            int dataFirstRowNum = point.DataFirstRowNum.Value;

            WriteCollectDataExistent(sheet, columnHeader, point, sheetType, style, headers, data, ref dataFirstRowNum);

            if (data == null || !data.Any(t => !string.IsNullOrEmpty(t.Department))) return;

            WriteCollectDataNonexistent(sheet, columnHeader, point, sheetType, style, headers, data, dataFirstRowNum);
        }

        private static void WriteCollectDataExistent(ISheet sheet, IRow columnHeader, PerSheetPoint point, SheetType sheetType, ExcelStyle style,
            List<string> headers, List<collect_data> data, ref int dataFirstRowNum)
        {
            if (sheet.LastRowNum <= dataFirstRowNum) return;

            var cellStyle = style.SetBgkColorAndFormat(style.GetCellStyle(), StyleType.数据);

            int dataFirstCellNum = point.DataFirstCellNum.Value;

            for (int rowIndex = dataFirstRowNum; rowIndex < sheet.LastRowNum + 1; rowIndex++)
            {
                var row = sheet.GetRow(rowIndex);
                if (row == null) continue;

                string department = row.GetOrCreate(dataFirstCellNum - 1).GetDecodeEscapes();
                if (string.IsNullOrEmpty(department)) continue;

                var deptData = data.Where(t => t.Department == department);
                if (deptData == null || !deptData.Any()) continue;

                for (int cellIndex = dataFirstCellNum; cellIndex < columnHeader.LastCellNum; cellIndex++)
                {
                    var column = columnHeader.GetOrCreate(cellIndex).GetDecodeEscapes();
                    var cell = row.GetOrCreate(cellIndex);

                    var value = deptData.FirstOrDefault(t => t.TypeName == column)?.CellValue;

                    if (sheetType == SheetType.Income)
                    {
                        cell.SetCellValue<decimal>(value);
                        cell.CellStyle = cellStyle;
                    }
                    else if (cell.CellType != CellType.Formula)
                    {
                        cell.SetCellValue<decimal>(value);
                        if (headers != null && headers.Contains(column))
                        {
                            cell.CellStyle = cellStyle;
                        }
                    }
                }

                data.RemoveAll(t => t.Department == department);
                if (rowIndex > dataFirstRowNum) dataFirstRowNum = rowIndex + 1;
            }
        }

        private static void WriteCollectDataNonexistent(ISheet sheet, IRow columnHeader, PerSheetPoint point, SheetType sheetType, ExcelStyle style,
            List<string> headers, List<collect_data> data, int dataFirstRowNum)
        {
            var departments = data.Select(s => s.Department).Where(w => !string.IsNullOrEmpty(w)).Distinct().ToList();

            var filed = sheet.SheetName.Contains("工作量") ? collectWork : collectIncome;

            var deptStyle = style.GetCellStyle();
            var cellStyle = style.SetBgkColorAndFormat(style.GetCellStyle(), StyleType.数据);

            foreach (string department in departments)
            {
                var deptData = data.Where(t => t.Department == department);
                if (deptData == null || !deptData.Any()) continue;

                var row = sheet.GetOrCreate(dataFirstRowNum);
                for (int cellIndex = point.HeaderFirstCellNum.Value; cellIndex < columnHeader.LastCellNum; cellIndex++)
                {
                    var column = columnHeader.GetCell(cellIndex).GetDecodeEscapes();
                    var cell = row.CreateCell(cellIndex);

                    if (filed.ContainsKey(column))
                    {
                        cell.SetCellOValue(filed[column]?.Invoke(deptData.First()));
                        cell.CellStyle = deptStyle;
                    }
                    else if (sheetType == SheetType.Income || (headers != null && headers.Contains(column)))
                    {
                        var value = deptData.FirstOrDefault(t => t.TypeName == column)?.CellValue;
                        cell.SetCellValue<decimal>(value);
                        cell.CellStyle = cellStyle;
                    }
                }
                dataFirstRowNum++;
            }
        }

        /// <summary> 收入固定列 </summary>
        private static readonly Dictionary<string, Func<collect_data, string>> collectIncome = new Dictionary<string, Func<collect_data, string>>
        {
            { "科室名称", (dto) => dto.Department },
            { "核算单元（医生组）", (dto) => dto.AccountingUnitDoctor },
            { "核算单元（护理组）", (dto) => dto.AccountingUnitNurse },
            { "核算单元（医技组）", (dto) => dto.AccountingUnitTechnician },
        };

        /// <summary> 工作量固定列 </summary>
        private static readonly Dictionary<string, Func<collect_data, string>> collectWork = new Dictionary<string, Func<collect_data, string>>
        {
            { "科室名称", (dto) => dto.Department },
            { "核算单元", (dto) => dto.AccountingUnitDoctor },
        };

        #endregion
    }
}
