using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.IO;
using System.Linq;
using System.Runtime.Caching;
using Aspose.Cells;
using Kreta.BusinessLogic.Classes;
using Kreta.BusinessLogic.HelperClasses;
using Kreta.BusinessLogic.HelperClasses.ImportCo;
using Kreta.BusinessLogic.Logic;
using Kreta.Core;
using Kreta.Core.ConnectionType;
using Kreta.DataAccessManual;
using Kreta.DataAccessManual.Util;
using Kreta.Enums;
using Kreta.Enums.ManualEnums;
using Kreta.Enums.ManualEnums.ImportExport;
using Kreta.Resources;

namespace Kreta.BusinessLogic.Helpers.ImportExport
{
    public class TanuloErtekelesMondatbankImportExportHelper : LogicBase
    {
        #region Fields

        private readonly string _importObjectCacheKey;

        #endregion Fields

        #region Properties

        public static Dictionary<int, string> ImportHeaderList => new Dictionary<int, string>
        {
            { 00, ImportExportTanuloErtekelesMondatbankResource.ImportHeaderNameRovidNev },
            { 01, ImportExportTanuloErtekelesMondatbankResource.ImportHeaderNameSzoveg },
            { 02, ImportExportTanuloErtekelesMondatbankResource.ImportHeaderNameTanuloErtekelesTipus },
            { 03, ImportExportTanuloErtekelesMondatbankResource.ImportHeaderNameEvfolyamTipus },
            { 04, ImportExportTanuloErtekelesMondatbankResource.ImportHeaderNameTantargy }
        };

        public static Dictionary<int, string> ExportHeaderList => new Dictionary<int, string>(ImportHeaderList);

        public TanuloErtekelesMondatbankImportCo ImportCo
        {
            get => (TanuloErtekelesMondatbankImportCo)Cache.Get(_importObjectCacheKey);
            set
            {
                if (ImportCo != null)
                {
                    Cache.Remove(_importObjectCacheKey);
                }

                Cache.Add(_importObjectCacheKey, value, new CacheItemPolicy { SlidingExpiration = TimeSpan.FromMinutes(30) });
            }
        }

        #endregion Properties

        #region Constructors

        public TanuloErtekelesMondatbankImportExportHelper(IConnectionType connectionType) : base(connectionType)
        {
            _importObjectCacheKey = $"{nameof(TanuloErtekelesMondatbankImportCo)}_{IntezmenyId}_{FelhasznaloId}_ImportObjectCacheKey";
        }

        #endregion Constructors

        public void SetImportCo(List<List<string>> importData, int importMuvelet)
        {
            TanuloErtekelesMondatbankImportCo importCo = GetImportCoFromImportData(importData);
            importCo.ImportMuvelet = importMuvelet;

            SetNemImportalhatoSorokByValidation(importCo);

            SetOperationAndNemImportalhatoSorokByOperation(importCo);

            var mainImportJsonItemList = new List<TanuloErtekelesMondatbankImportJsonItemCo>(importCo.MainImportItemList.Select(x => new TanuloErtekelesMondatbankImportJsonItemCo(x, TanevId, IntezmenyId, FelhasznaloId)));
            importCo.MainImportJsonItemList = mainImportJsonItemList;

            ImportCo = importCo;
        }

        private static void SetNemImportalhatoSorokByValidation(TanuloErtekelesMondatbankImportCo importCo)
        {
            Dictionary<int, List<ValidationResult>> validationResultDictionary = importCo.Validate();

            //NOTE: Azokat a sorokat, amelyek hibásak beletesszük a NemImportalhatoItemList-be!
            foreach (TanuloErtekelesMondatbankImportItemCo importItem in importCo.MainImportItemList.Where(x => validationResultDictionary.Keys.Contains(x.LineNumber)))
            {
                IEnumerable<ValidationResult> validationResultList = validationResultDictionary[importItem.LineNumber];
                importItem.ErrorList = validationResultList.Select(x => x.ErrorMessage).ToList();
                importCo.NemImportalhatoItemList.Add(importItem);
            }

            //NOTE: Azokat a sorokat, amelyek bekerültek a NemImportalhatoItemList-be, azokat kiveszzük a MainImportItemList-ből!
            importCo.MainImportItemList.RemoveRange(importCo.NemImportalhatoItemList);
        }

        private void SetOperationAndNemImportalhatoSorokByOperation(TanuloErtekelesMondatbankImportCo importCo)
        {
            int importMuvelet = importCo.ImportMuvelet;
            //NOTE: Azért van szükség Dictionary-re, mert így sokkal gyorsabb a keresés!
            Dictionary<string, ImportItemCompareCo> compareHashImportItemCompareCoDictionary = GetCompareHashImportItemCompareCoDictionary(importCo.TanuloErtekelesMondatbankCoList);
            foreach (TanuloErtekelesMondatbankImportItemCo importItem in importCo.MainImportItemList)
            {
                string compareHash = importItem.CompareHash;
                //NOTE: Ha nem találtunk egyezést, akkor mindeképpen Insert!
                if (!compareHashImportItemCompareCoDictionary.ContainsKey(compareHash))
                {
                    importItem.Operation = (int)ImportItemOperationEnum.Insert;
                }
                //NOTE: Ha találtunk egyezést, akkor...
                else
                {
                    ImportItemCompareCo importItemCompareCo = compareHashImportItemCompareCoDictionary[compareHash];
                    //NOTE: ...ha az import művelet DeleteAndInsert, akkor Insert, mert töröljük a korábbi elemeket (importált és nem importált) és újra be kell szúrni!
                    if (importMuvelet == (int)ImportMuveletEnum.DeleteAndInsert)
                    {
                        importItem.Operation = (int)ImportItemOperationEnum.Insert;
                    }
                    //NOTE: ...ha nincs törlés, akkor...
                    else
                    {
                        //NOTE: ...ha a művelet InsertAndUpdate, akkor Update és beállítjuk az Id-t!
                        if (importMuvelet == (int)ImportMuveletEnum.InsertAndUpdate)
                        {
                            importItem.Id = importItemCompareCo.Id;
                            importItem.Operation = (int)ImportItemOperationEnum.Update;
                        }
                        //NOTE: ...ha a művelet nem InsertAndUpdate, akkor hozzá kell adni a nem NemImportalhatoItemList-hez és jelezni, hogy már szerepel az adatbázisban(Insert vagy DeleteAndInsert műveletnél. A DeleteAndInsert csak akkor fut be ide, ha az elem nem importált.)!
                        else
                        {
                            importItem.ErrorList.Add(ImportExportCommonResource.NemImportalhatoMertMarSzerepelAzAdatbazisban);
                            importCo.NemImportalhatoItemList.Add(importItem);
                        }
                    }
                }
            }

            //NOTE: Azokat a sorokat, amelyek bekerültek a NemImportalhatoItemList-be, azokat kiveszzük a MainImportItemList-ből!
            importCo.MainImportItemList.RemoveRange(importCo.NemImportalhatoItemList);
        }

        private TanuloErtekelesMondatbankImportCo GetImportCoFromImportData(List<List<string>> importData)
        {
            var importCo = new TanuloErtekelesMondatbankImportCo
            {
                TanuloErtekelesMondatbankCoList = new TanuloErtekelesMondatbankHelper(ConnectionType).GetTanuloErtekelesMondatbankCoList()
            };

            int lineNumber = 1;
            foreach (List<string> importDataRow in importData.Skip(1))
            {
                var importItemCo = new TanuloErtekelesMondatbankImportItemCo
                {
                    LineNumber = lineNumber,

                    RovidNevImportData = importDataRow[ImportHeaderList.GetKeyByUniqueValue(ImportExportTanuloErtekelesMondatbankResource.ImportHeaderNameRovidNev)],
                    SzovegImportData = importDataRow[ImportHeaderList.GetKeyByUniqueValue(ImportExportTanuloErtekelesMondatbankResource.ImportHeaderNameSzoveg)],
                    TanuloErtekelesTipusImportData = importDataRow[ImportHeaderList.GetKeyByUniqueValue(ImportExportTanuloErtekelesMondatbankResource.ImportHeaderNameTanuloErtekelesTipus)],
                    EvfolyamTipusImportData = importDataRow[ImportHeaderList.GetKeyByUniqueValue(ImportExportTanuloErtekelesMondatbankResource.ImportHeaderNameEvfolyamTipus)],
                    TantargyImportData = importDataRow[ImportHeaderList.GetKeyByUniqueValue(ImportExportTanuloErtekelesMondatbankResource.ImportHeaderNameTantargy)],
                };

                // NOTE: Az értékelés típus felhasználó által bővíthető, ezért a DB-ből kell lekérdeznünk az értékeket.
                KeyValuePair<int, string> ertekelesTipus = EnumExtensions.GetAdatszotarDictElemekForTipus(TanevId, GeneratedAdatszotarTipusEnum.ErtekelesTipus).SingleOrDefault(x => x.Value == importItemCo.TanuloErtekelesTipusNev);
                importItemCo.TanuloErtekelesTipusId = !ertekelesTipus.Equals(default(KeyValuePair<int, string>)) ? ertekelesTipus.Key : (int?)null;

                importItemCo.EvfolyamTipusId = EnumExtensions.EnumToDictionary<EvfolyamTipusEnum>(TanevId).SingleOrDefault(x => x.Value == importItemCo.EvfolyamTipusNev).Key.ToNullableInt();

                var tantargyCoList = new TantargyHelper(ConnectionType).GetTantargyCoList();
                var tantargyNevToCompare = importItemCo.TantargyNev?.ToComparableString();
                var tantargy = tantargyCoList.SingleOrDefault(x => x.NevComparableString == tantargyNevToCompare);
                importItemCo.TantargyId = tantargy?.Id;

                if (tantargyNevToCompare == TanuloErtekelesResource.Magatartas.ToComparableString())
                {
                    importItemCo.TipusId = (int)ErtMondatbankTipusEnum.Magatartas;
                }
                else if (tantargyNevToCompare == TanuloErtekelesResource.Szorgalom.ToComparableString())
                {
                    importItemCo.TipusId = (int)ErtMondatbankTipusEnum.Szorgalom;
                }
                else
                {
                    importItemCo.TipusId = (int)ErtMondatbankTipusEnum.Tantargy;
                }

                string md5HashInput =
                    importItemCo.RovidNev?.ToComparableString() +
                    importItemCo.Szoveg?.ToComparableString() +
                    importItemCo.TanuloErtekelesTipusNev?.ToComparableString() +
                    importItemCo.EvfolyamTipusNev?.ToComparableString() +
                    importItemCo.TantargyNev?.ToComparableString();
                importItemCo.CompareHash = ImportExportHelper.GetMd5Hash(md5HashInput);

                importCo.MainImportItemList.Add(importItemCo);

                lineNumber++;
            }

            return importCo;
        }

        public MemoryStream GetTemplate(bool isSzakkepzo = false, bool isFromSzervezet = false)
        {
            var dropDownColumnSourceDictionary = GetDefaultDropDownColumnSourceDictionary(isSzakkepzo: isSzakkepzo, isFromSzervezet: isFromSzervezet);

            MemoryStream memoryStream = ImportExportHelper.GetTemplate(ImportExportTanuloErtekelesMondatbankResource.ImportDefaultSheetName, ImportHeaderList, dropDownColumnSourceDictionary);

            return memoryStream;
        }

        public MemoryStream GetExport(bool isSzakkepzo = false)
        {
            var dropDownColumnSourceDictionary = GetDefaultDropDownColumnSourceDictionary(isSzakkepzo: isSzakkepzo);

            List<TanuloErtekelesMondatbankItemCo> coList = new TanuloErtekelesMondatbankHelper(ConnectionType).GetTanuloErtekelesMondatbankCoList();
            using (var workbook = new Workbook())
            {
                var worksheet = ImportExportHelper.GetWorksheetsWithDropDownFormula(workbook, ImportExportTanuloErtekelesMondatbankResource.ImportDefaultSheetName, ExportHeaderList, dropDownColumnSourceDictionary);

                var columnToWrapIndexList = new List<int>();
                int rowNumber = 1;
                foreach (TanuloErtekelesMondatbankItemCo co in coList.OrderBy(d => d.RovidNev).ThenBy(d => d.Szoveg))
                {
                    worksheet.Cells[rowNumber, ExportHeaderList.GetKeyByUniqueValue(ImportExportTanuloErtekelesMondatbankResource.ImportHeaderNameRovidNev)].Value = co.RovidNev;

                    int szovegCellColumnIndex = ExportHeaderList.GetKeyByUniqueValue(ImportExportTanuloErtekelesMondatbankResource.ImportHeaderNameSzoveg);
                    Cell szovegCell = worksheet.Cells[rowNumber, szovegCellColumnIndex];
                    szovegCell.HtmlString = co.Szoveg;
                    szovegCell.SetTextWrap();
                    columnToWrapIndexList.Add(szovegCellColumnIndex);

                    worksheet.Cells[rowNumber, ExportHeaderList.GetKeyByUniqueValue(ImportExportTanuloErtekelesMondatbankResource.ImportHeaderNameTanuloErtekelesTipus)].Value = co.TanuloErtekelesTipusNev;
                    worksheet.Cells[rowNumber, ExportHeaderList.GetKeyByUniqueValue(ImportExportTanuloErtekelesMondatbankResource.ImportHeaderNameEvfolyamTipus)].Value = co.EvfolyamTipusNev;
                    worksheet.Cells[rowNumber, ExportHeaderList.GetKeyByUniqueValue(ImportExportTanuloErtekelesMondatbankResource.ImportHeaderNameTantargy)].Value = co.TantargyNev;

                    rowNumber++;
                }

                var sheetCodeNameAndColumnsToWrapIndexDictionary = new Dictionary<string, List<int>>
                {
                    { workbook.Worksheets[0].CodeName, columnToWrapIndexList }
                };
                MemoryStream memoryStream = SimpleExportLogic.GetWorkbookMemoryStream(workbook, sheetCodeNameAndColumnsToWrapIndexDictionary);
                return memoryStream;
            }
        }

        public MemoryStream GetNemImportalhatoSorokExport()
        {
            using (var workbook = new Workbook())
            {
                var worksheet = ImportExportHelper.GetWorksheetsWithDropDownFormula(workbook, ImportExportTanuloErtekelesMondatbankResource.ImportDefaultSheetName, ImportHeaderList);

                var columnToWrapIndexList = new List<int>();
                int rowNumber = 1;
                foreach (TanuloErtekelesMondatbankImportItemCo co in ImportCo.NemImportalhatoItemList)
                {
                    worksheet.Cells[rowNumber, ExportHeaderList.GetKeyByUniqueValue(ImportExportTanuloErtekelesMondatbankResource.ImportHeaderNameRovidNev)].Value = co.RovidNev;

                    int szovegCellColumnIndex = ExportHeaderList.GetKeyByUniqueValue(ImportExportTanuloErtekelesMondatbankResource.ImportHeaderNameSzoveg);
                    Cell szovegCell = worksheet.Cells[rowNumber, szovegCellColumnIndex];
                    szovegCell.Value = co.Szoveg;
                    szovegCell.SetTextWrap();
                    columnToWrapIndexList.Add(szovegCellColumnIndex);

                    worksheet.Cells[rowNumber, ExportHeaderList.GetKeyByUniqueValue(ImportExportTanuloErtekelesMondatbankResource.ImportHeaderNameTanuloErtekelesTipus)].Value = co.TanuloErtekelesTipusNev;
                    worksheet.Cells[rowNumber, ExportHeaderList.GetKeyByUniqueValue(ImportExportTanuloErtekelesMondatbankResource.ImportHeaderNameEvfolyamTipus)].Value = co.EvfolyamTipusNev;
                    worksheet.Cells[rowNumber, ExportHeaderList.GetKeyByUniqueValue(ImportExportTanuloErtekelesMondatbankResource.ImportHeaderNameTantargy)].Value = co.TantargyNev;

                    int errorCellColumnIndex = ImportHeaderList.Count;
                    Cell errorCell = worksheet.Cells[rowNumber, ImportHeaderList.Count];
                    errorCell.Value = string.Join(Environment.NewLine, co.ErrorList);
                    errorCell.SetTextWrap();
                    columnToWrapIndexList.Add(errorCellColumnIndex);

                    rowNumber++;
                }

                var sheetCodeNameAndColumnsToWrapIndexDictionary = new Dictionary<string, List<int>>
                {
                    { workbook.Worksheets[0].CodeName, columnToWrapIndexList }
                };
                MemoryStream memoryStream = SimpleExportLogic.GetWorkbookMemoryStream(workbook, sheetCodeNameAndColumnsToWrapIndexDictionary);
                return memoryStream;
            }
        }

        private Dictionary<string, ImportItemCompareCo> GetCompareHashImportItemCompareCoDictionary(List<TanuloErtekelesMondatbankItemCo> coList)
        {
            var compareHashImportItemCompareCoDictionary = new Dictionary<string, ImportItemCompareCo>();
            foreach (TanuloErtekelesMondatbankItemCo co in coList)
            {
                string md5HashInput =
                    co.RovidNevComparableString +
                    co.SzovegComparableString +
                    co.TanuloErtekelesTipusNevComparableString +
                    co.EvfolyamTipusNevComparableString +
                    co.TantargyNevComparableString;
                string compareHash = ImportExportHelper.GetMd5Hash(md5HashInput);

                var importItemCompareCo = new ImportItemCompareCo
                {
                    Id = co.Id,
                    CompareHash = compareHash,
                    Importalt = co.Importalt
                };
                //NOTE: Elvileg nem fordulhatna elő, de ha az adatbázisban duplikált adat van, akkor csak az elsőt adjuk hozzá!
                if (!compareHashImportItemCompareCoDictionary.ContainsKey(compareHash))
                {
                    compareHashImportItemCompareCoDictionary.Add(compareHash, importItemCompareCo);
                }
            }

            return compareHashImportItemCompareCoDictionary;
        }

        private Dictionary<string, IList<string>> GetDefaultDropDownColumnSourceDictionary(bool isSzakkepzo = false, bool isFromSzervezet = false)
        {
            //NOTE: Create dropdown lists
            var logicalColumnDictionary = new Dictionary<string, IList<string>>();
            var dropDownColumnSourceDictionary = new Dictionary<string, IList<string>>(logicalColumnDictionary);

            Dal.CustomConnection.Run(ConnectionType, dalHandler =>
            {
                var helper = new ImportExportHelper(new DalHandlerConnectionType(ConnectionType, dalHandler));

                dropDownColumnSourceDictionary.Add(ImportExportTanuloErtekelesMondatbankResource.ImportHeaderNameTanuloErtekelesTipus, helper.GetTanuloErtekelesTipusList());
                dropDownColumnSourceDictionary.Add(ImportExportTanuloErtekelesMondatbankResource.ImportHeaderNameEvfolyamTipus, helper.GetEvfolyamTipusList());
                dropDownColumnSourceDictionary.Add(ImportExportTanuloErtekelesMondatbankResource.ImportHeaderNameTantargy, helper.GetTantargyList(true, isFromSzervezet: isFromSzervezet, isSzakkepzo: isSzakkepzo));
            });

            return dropDownColumnSourceDictionary;
        }
    }
}