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 AccountBasicDataWrite : ISheetDataWrite
    {
        private readonly ILogger logger;

        public AccountBasicDataWrite(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)
        {
        }

        public void WriteSheetData(ISheet sheet, PerSheetPoint point, SheetType sheetType, ExcelStyle style, object data, Dictionary<ExDataDict, object> exdict = null)
        {
            try
            {
                if (!exdict.ContainsKey(ExDataDict.AccountingBasic))
                    exdict.Add(ExDataDict.AccountingBasic, new List<Account>());

                var accountingUnits = new List<Account>();

                if (data != null && data is List<per_dept_dic> departments && departments.Any())
                {
                    logger.LogInformation("4.1所有系统科室: " + JsonHelper.Serialize(departments));

                    int dataFirstRowNum = point.DataFirstRowNum.Value;

                    accountingUnits = departments.Select(t => new Account
                    {
                        UnitType = t.UnitType.NoBlank(),
                        AccountingUnit = t.AccountingUnit.NoBlank()
                    }).ToList();

                    var tuples = GetAccountingUnitDataNonexistent(sheet, point, accountingUnits);

                    accountingUnits = accountingUnits.Where(t => !t.AccountingUnit.Contains("非核算"))?.ToList();

                    if (accountingUnits != null && accountingUnits.Any())
                        WriteAccountingUnitDataNonexistent(sheet, point, style, accountingUnits, tuples);
                }

                logger.LogInformation("4.1所有科室: " + JsonHelper.Serialize(accountingUnits));

                var accountBasic = new List<Account>();

                for (int i = point.DataFirstRowNum.Value; i < sheet.LastRowNum + accountingUnits.Count + 1; i++)
                {
                    var row = sheet.GetRow(i);
                    if (row == null) continue;

                    accountBasic.Add(new Account
                    {
                        UnitType = row.GetCell(point.DataFirstCellNum.Value - 2).GetDecodeEscapes(),
                        AccountingUnit = row.GetCell(point.DataFirstCellNum.Value - 1).GetDecodeEscapes()
                    });
                }

                exdict[ExDataDict.AccountingBasic] = accountBasic;
            }
            catch (Exception ex)
            {
                logger.LogError("4.1所有科室被获取时发生异常:" + ex.ToString());
            }
        }

        private List<Account> GetAccountingUnitDataNonexistent(ISheet sheet, PerSheetPoint point, List<Account> accountingUnits)
        {
            var tuples = new List<Account>();

            for (int i = point.DataFirstRowNum.Value; i < sheet.LastRowNum + 1; i++)
            {
                var row = sheet.GetRow(i);
                if (row == null) continue;

                var unittype = row.GetCell(point.DataFirstCellNum.Value - 2).GetDecodeEscapes();
                var accountingunit = row.GetCell(point.DataFirstCellNum.Value - 1).GetDecodeEscapes();

                if (!tuples.Any())
                    tuples.Add(new Account(unittype, i, i));
                else
                {
                    var last = tuples.Last();
                    if (last.UnitType == unittype)
                        last.EndIndex = i;
                    else
                        tuples.Add(new Account(unittype, i, i));
                }

                var account = accountingUnits.FirstOrDefault(t => t.AccountingUnit == accountingunit && t.UnitType == unittype);
                if (account != null) accountingUnits.Remove(account);
            }

            return tuples;
        }

        private void WriteAccountingUnitDataNonexistent(ISheet sheet, PerSheetPoint point, ExcelStyle style, List<Account> accountingUnits, List<Account> tuples)
        {
            int number = 0;
            var deptStyle = style.GetCellStyle();
            foreach (var unitType in tuples.Select(t => t.UnitType).Distinct())
            {
                var accountingList = accountingUnits.Where(t => t.UnitType == unitType)?.ToList();
                if (accountingList == null || !accountingList.Any()) continue;

                var tuple = tuples.First(t => t.UnitType == unitType);

                sheet.ShiftRows(tuple.EndIndex + 1 + number, sheet.LastRowNum + number, accountingList.Count);

                int index = tuple.EndIndex + 1 + number;
                foreach (var acccount in accountingList)
                {
                    var row = sheet.GetOrCreate(index);

                    var unitTypeCell = row.GetOrCreate(point.DataFirstCellNum.Value - 2);
                    unitTypeCell.SetCellValue<string>(acccount.UnitType);
                    unitTypeCell.CellStyle = deptStyle;

                    var accountingUnitCell = row.GetOrCreate(point.DataFirstCellNum.Value - 1);
                    accountingUnitCell.SetCellValue<string>(acccount.AccountingUnit);
                    accountingUnitCell.CellStyle = deptStyle;

                    index++;
                }

                number += accountingList.Count;
            }
        }
    }

    public class Account
    {
        public Account()
        {
        }

        public Account(string unitType, int beginIndex, int endIndex)
        {
            UnitType = unitType;
            BeginIndex = beginIndex;
            EndIndex = endIndex;
        }

        public string UnitType { get; set; }

        public string AccountingUnit { get; set; }

        public int BeginIndex { get; set; }

        public int EndIndex { get; set; }
    }
}
