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

namespace Performance.Services.ExtractExcelService.SheetDataWrite
{
    public class CostTransferDataWrite : IAutoInjection
    {
        private readonly ILogger logger;
        private readonly PerforCosttransferRepository costtransferRepository;
        private readonly PerforCosttransferitemRepository costtransferitemRepository;
        private readonly PerforPerdeptdicRepository perdeptdicRepository;

        public CostTransferDataWrite(
            ILogger<CostTransferDataWrite> logger,
            PerforCosttransferRepository costtransferRepository,
            PerforCosttransferitemRepository costtransferitemRepository,
            PerforPerdeptdicRepository perdeptdicRepository
            )
        {
            this.logger = logger;
            this.costtransferRepository = costtransferRepository;
            this.costtransferitemRepository = costtransferitemRepository;
            this.perdeptdicRepository = perdeptdicRepository;
        }

        private readonly string[] Unittypes = new string[] { UnitType.护理组.ToString(), UnitType.医生组.ToString(), UnitType.医技组.ToString() };

        public void WriteSheetData(ISheet sheet, PerSheetPoint point, ExcelStyle style, int allotId, int hospitalId)
        {
            try
            {
                var costTransfers = costtransferRepository.GetEntities(t => t.AllotId == allotId);
                if (costTransfers == null || !costTransfers.Any()) return;

                var costTransferItems = costtransferitemRepository.GetEntities(t => costTransfers.Select(c => c.Id).Contains(t.TransferId) && t.Status == 1 && t.AdminStatus == 1);
                if (costTransferItems == null || !costTransferItems.Any()) return;

                var columns = SupplySheetHeader(sheet, point);
                if (columns == null || !columns.Any()) return;

                var departments = costTransfers.Select(t => t.AdoptedDepartment ?? "").Union(costTransfers.Select(t => t.ApplicantDepartment ?? "")).Distinct().ToList();

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

                WriteSheetDataExistent(sheet, cellStyle, point, columns, departments, costTransfers, costTransferItems);

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

                var standardDict = perdeptdicRepository.GetEntities(t => t.HospitalId == hospitalId && departments.Contains(t.Department)) ?? new List<per_dept_dic>();

                WriteSheetDataNonexistent(sheet, cellStyle, point, columns, departments, standardDict, costTransfers, costTransferItems);
            }
            catch (Exception ex)
            {
                logger.LogError("写入划拨数据异常：" + ex);
            }
        }

        private Dictionary<string, int> SupplySheetHeader(ISheet sheet, PerSheetPoint point)
        {
            Dictionary<string, int> pairs = new Dictionary<string, int>();
            var header = sheet.GetRow(point.HeaderFirstRowNum.Value);
            if (header == null) return pairs;

            List<string> fixedColumns = new List<string> { $"划拨收入（{UnitType.医技组}）", $"划拨收入（{UnitType.医生组}）", $"划拨收入（{UnitType.护理组}）" };

            for (int i = point.HeaderFirstCellNum.Value; i < header.LastCellNum; i++)
            {
                var column = header.GetCell(i)?.GetDecodeEscapes().Replace("(", "（").Replace(")", "）");
                if (!string.IsNullOrEmpty(column) && fixedColumns.Any(t => t == column))
                {
                    if (!pairs.ContainsKey(column))
                        pairs.Add(column, i);

                    fixedColumns.Remove(column);
                }
            }

            if (fixedColumns != null && fixedColumns.Any())
            {
                var index = header.LastCellNum + 1;
                foreach (var column in fixedColumns)
                {
                    var cell = header.CreateCell(index);
                    cell.SetCellValue(column);

                    if (!pairs.ContainsKey(column))
                        pairs.Add(column, index);

                    index++;
                }
            }

            return pairs;
        }

        private void WriteSheetDataExistent(ISheet sheet, ICellStyle style, PerSheetPoint point, Dictionary<string, int> columns, List<string> departments,
            List<cost_transfer> costTransfers, List<cost_transfer_item> costTransferItems)
        {
            for (int i = point.DataFirstRowNum.Value; i < sheet.LastRowNum + 1; i++)
            {
                var row = sheet.GetOrCreate(i);
                var department = row.GetOrCreate(point.DataFirstCellNum.Value - 1).GetDecodeEscapes();

                foreach (var unittype in Unittypes)
                {
                    var key = columns.Keys.FirstOrDefault(t => t.Contains(unittype));
                    if (string.IsNullOrEmpty(key)) continue;

                    var index = columns[key];
                    var cell = row.GetOrCreate(index);

                    var score = GetTransfersByDeptAndUnittype(costTransfers, costTransferItems, department, unittype);
                    cell.SetCellOValue(score);
                    cell.CellStyle = style;
                }

                departments.Remove(department);
            }
        }

        private void WriteSheetDataNonexistent(ISheet sheet, ICellStyle style, PerSheetPoint point, Dictionary<string, int> columns, List<string> departments,
            List<per_dept_dic> standardDict, List<cost_transfer> costTransfers, List<cost_transfer_item> costTransferItems)
        {
            int indexrow = sheet.LastRowNum + 1;
            Dictionary<string, int> accountingUnits = new Dictionary<string, int>
                {
                    { UnitType.护理组.ToString(), 2 } ,{ UnitType.医生组.ToString(), 3 } ,{ UnitType.医技组.ToString(), 4 },
                };
            foreach (var department in departments)
            {
                var row = sheet.GetOrCreate(indexrow);

                foreach (var unittype in Unittypes)
                {
                    var key = columns.Keys.FirstOrDefault(t => t.Contains(unittype));
                    if (!string.IsNullOrEmpty(key) && columns.ContainsKey(key))
                    {
                        var index = columns[key];
                        var cell = row.GetOrCreate(index);

                        var score = GetTransfersByDeptAndUnittype(costTransfers, costTransferItems, department, unittype);
                        cell.SetCellOValue(score);
                        cell.CellStyle = style;
                    }
                    if (accountingUnits.ContainsKey(unittype))
                    {
                        var cell = row.GetOrCreate(point.DataFirstRowNum.Value - accountingUnits[unittype]);
                        var value = standardDict.Where(t => t.Department == department && t.UnitType == unittype)
                            ?.Select(t => t.AccountingUnit).FirstOrDefault(t => !string.IsNullOrEmpty(t)) ?? "";
                        cell.SetCellValue(value);
                        cell.CellStyle = style;
                    }
                }

                indexrow++;
            }
        }

        private decimal GetTransfersByDeptAndUnittype(List<cost_transfer> costTransfers, List<cost_transfer_item> costTransferItems, string department, string unittype)
        {
            if (costTransfers == null || !costTransfers.Any()) return 0;

            var isApplications = new bool[] { true, false };
            var scores = new List<decimal>();
            foreach (var isApplication in isApplications)
            {
                var data = isApplication
                ? costTransfers.Where(t => t.ApplicantDepartment == department && t.ApplicantUnitType == unittype)?.ToList()
                : costTransfers.Where(t => t.AdoptedDepartment == department && t.AdoptedUnitType == unittype)?.ToList();

                if (data == null || !data.Any()) continue;

                var items = costTransferItems.Where(t => data.Select(s => s.Id).Contains(t.TransferId))?.ToList();
                if (items == null || !items.Any()) continue;

                items.ForEach(t => t.IsWrited = 1);
                costtransferitemRepository.UpdateRange(items.ToArray());

                int sign = isApplication ? 1 : -1;
                var value = items.Sum(t => (t.CalculationAmount ?? 0) * sign);
                scores.Add(value);
            }
            return scores.Sum(t => t);
        }
    }
}
