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

namespace Performance.Services.ExtractExcelService.SheetDataWrite
{
    public class AccountExtraDataWrite : ISheetDataWrite
    {
        private readonly ILogger logger;

        public AccountExtraDataWrite(ILogger logger)
        {
            this.logger = logger;
        }

        public void WriteCollectData(ISheet sheet, PerSheetPoint point, SheetType sheetType, ExcelStyle style, List<collect_data> collects, Dictionary<ExDataDict, object> exdict = null)
        {
            point = new PerSheetPoint
            {
                HeaderFirstRowNum = 1,
                HeaderFirstCellNum = 0,
                DataFirstRowNum = 2,
                DataFirstCellNum = 0
            };

            var accounts = new List<Account>();
            if (exdict != null && exdict.ContainsKey(ExDataDict.AccountingBasic) && exdict[ExDataDict.AccountingBasic] != null)
                accounts = exdict[ExDataDict.AccountingBasic] as List<Account> ?? new List<Account>();

            ExtractHelper.ClearSheetPartialData(sheet, point, sheetType);

            var columns = new List<string>();
            if (collects != null && collects.Any(t => !string.IsNullOrEmpty(t.TypeName)))
            {
                columns = collects.Where(t => !string.IsNullOrEmpty(t.TypeName)).Select(t => t.TypeName).Distinct().OrderBy(t => t).ToList();

                var headers = columns.Select(t => new ExcelHeader
                {
                    ColumnName = t.NoBlank(),
                    DoctorFactor = 0,
                    NurseFactor = 0,
                    TechnicianFactor = 0
                }).ToList();

                WriteSheetHeader(sheet, point, sheetType, style, headers);
            }
            WriteCollectData(sheet, point, sheetType, style, columns, collects, accounts);
        }

        public void WriteSheetData(ISheet sheet, PerSheetPoint point, SheetType sheetType, ExcelStyle style, object data, Dictionary<ExDataDict, object> exdict = null)
        {
        }

        private 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 columnHeader = sheet.GetOrCreate(point.HeaderFirstRowNum.Value);

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

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

            if (columns == null || !columns.Any()) return;

            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;

                headerFirstCellNum++;
            }
        }

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

            data = data ?? new List<collect_data>();

            accounts = accounts.Any() ? accounts : data.Select(s => new Account { AccountingUnit = s.Department, UnitType = s.UnitType })
                .Where(w => !string.IsNullOrEmpty(w.AccountingUnit)).ToDistinct().ToList();

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

            headers = headers.Select(t => t.NoBlank()).ToList();

            foreach (var account in accounts)
            {
                var deptData = data.Where(t => t.AccountingUnitDoctor == account.AccountingUnit && t.UnitType == account.UnitType);
                var hasData = deptData != null && deptData.Any();

                var row = sheet.GetOrCreate(dataFirstRowNum);

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

                    if (collectExtra.ContainsKey(column))
                    {
                        cell.SetCellOValue(collectExtra[column]?.Invoke(account));
                        cell.CellStyle = deptStyle;
                    }
                    else if (hasData && headers != null && headers.Contains(column))
                    {
                        var value = deptData.FirstOrDefault(t => t.TypeName.NoBlank() == column)?.CellValue;
                        cell.SetCellValue<decimal>(value);
                        cell.CellStyle = cellStyle;
                    }
                }
                dataFirstRowNum++;
            }
        }

        /// <summary> 收入固定列 </summary>
        private static readonly Dictionary<string, Func<Account, string>> collectExtra = new Dictionary<string, Func<Account, string>>
        {
            { "核算单元类型", (dto) => dto.UnitType },
            { "核算单元", (dto) => dto.AccountingUnit },
        };
    }
}
