using EgwCApp.Core;
using Newtonsoft.Json;
using static EgwCApp.Core.UstdData;
using static EgwCApp.Core.WharehouseData;

namespace EgwCApp.ExcImport
{
    public class ImportProc
    {
        #region Public Constructors

        /// <summary>
        /// Init oggetto per import
        /// </summary>
        /// <param name="confFileName"></param>
        public ImportProc(string confFileName)
        {
            if (!string.IsNullOrEmpty(confFileName))
            {
                fileConfName = confFileName;
            }
        }

        #endregion Public Constructors

        #region Public Methods

        /// <summary>
        /// Decodifica configurazione
        /// </summary>
        /// <returns></returns>
        public bool decodeConfig()
        {
            bool answ = false;
            if (!string.IsNullOrEmpty(fileConfName))
            {
                // deserializzo config
                if (!File.Exists(fileConfName))
                {
                    Console.WriteLine($"Error: ConfigFile not found | {fileConfName}");
                }
                else
                {
                    string rawData = File.ReadAllText(fileConfName);
                    // se ho contenuto procedo
                    if (string.IsNullOrEmpty(rawData))
                    {
                        Console.WriteLine($"Error: ConfigFile empty! | {fileConfName}");
                    }
                    else
                    {
                        // deserializzo
                        taskConfig = JsonConvert.DeserializeObject<ConfigFile>(rawData);
                        answ = taskConfig != null;
                    }
                }
            }
            return answ;
        }

        /// <summary>
        /// Esegue import (se possibile)
        /// </summary>
        /// <returns></returns>
        public bool doProcess()
        {
            bool answ = false;
            if (taskConfig != null)
            {
                // verifico esista il file...
                if (string.IsNullOrEmpty(taskConfig.FileInPath) && File.Exists(taskConfig.FileInPath))
                {
                    // manca file ingresso!!! esco!
                }
                else
                {
                    // verifico il tipo di process necessario...
                    switch (taskConfig.Type)
                    {
                        case ImportType.CSV:
                            fileReturnData = File.ReadAllText(taskConfig.FileInPath);
                            answ = true;
                            break;

                        case ImportType.Excel:
                            fileReturnData = processExcelImport(taskConfig.FileInPath, taskConfig.ReturnDataType);
                            answ = true;
                            break;

                        case ImportType.ND:
                        default:
                            break;
                    }
                }
            }
            return answ;
        }

        /// <summary>
        /// Esecuzione ritorno informazioni secondo configurazione...
        /// </summary>
        /// <returns></returns>
        public bool doReturn()
        {
            bool answ = false;
            if (taskConfig != null)
            {
                // verifico il tipo di return necessario...
                switch (taskConfig.Return)
                {
                    case ReturnMode.Console:
                        Console.WriteLine(fileReturnData);
                        answ = true;
                        break;

                    case ReturnMode.Redis:
                        // salvo in Redis!
                        string rKey = taskConfig.RedisOut;
                        RedisMan redMan = new RedisMan(taskConfig.RedisServer, taskConfig.RedisPort, taskConfig.RedisDB);
                        // salvo!
                        if (taskConfig.RedisTTL > 0)
                        {
                            redMan.setRSV(rKey, fileReturnData, taskConfig.RedisTTL);
                        }
                        else
                        {
                            redMan.setRSV(rKey, fileReturnData);
                        }
                        break;

                    case ReturnMode.File:
                        // verifico path ci sia... sennò creo
                        string outPath = string.IsNullOrEmpty(taskConfig.FileOutPath) ? "FileOut.txt" : taskConfig.FileOutPath;
                        // verifico se vadano salvati in una folder differente...
                        if (!string.IsNullOrEmpty(taskConfig.ConvertDir))
                        {
                            if (!Directory.Exists(taskConfig.ConvertDir))
                            {
                                Directory.CreateDirectory(taskConfig.ConvertDir);
                            }
                            outPath = Path.Combine(taskConfig.ConvertDir, Path.GetFileName(outPath));
                        }
                        // salvo il file!
                        File.WriteAllText(outPath, fileReturnData);
                        answ = true;
                        break;

                    case ReturnMode.ND:
                    default:
                        break;
                }
                // se fatto eventualmente archivio
                if (answ)
                {
                    if (!string.IsNullOrEmpty(taskConfig.ArchiveDir))
                    {
                        // folder archivio con dataora...
                        string folderArch = Path.Combine(taskConfig.ArchiveDir, $"{DateTime.Now:yyyyMMdd_HHmmss}");
                        // verifico cartella archivio
                        if (!Directory.Exists(folderArch))
                        {
                            Directory.CreateDirectory(folderArch);
                        }
                        // sposto file
                        string fName = Path.GetFileName(taskConfig.FileInPath);
                        File.Move(taskConfig.FileInPath, Path.Combine(folderArch, fName), true);
                    }
                }
            }
            return answ;
        }

        #endregion Public Methods

        #region Protected Properties

        /// <summary>
        /// Nome del file config da processare
        /// </summary>
        protected string fileConfName { get; set; } = "";

        /// <summary>
        /// Contenuto del file da restituire come return data (serializzato)
        /// </summary>
        protected string fileReturnData { get; set; } = "";

        /// <summary>
        /// Configurazione del task da eseguire
        /// </summary>
        protected ConfigFile? taskConfig { get; set; } = new ConfigFile();

        #endregion Protected Properties

        #region Protected Methods

        /// <summary>
        /// Estrae da una riga l'i-esimo elemento
        /// </summary>
        /// <param name="riga"></param>
        /// <param name="col"></param>
        /// <returns></returns>
        protected string getCellVal(System.Data.DataRow? riga, int col)
        {
            string answ = "";
            if (riga != null)
            {
                try
                {
                    answ = $"{riga.ItemArray[col]}".Trim();
                }
                catch
                { }
            }
            return answ;
        }

        /// <summary>
        /// Cleanup stringa x impiego tipo ident da char dubbi
        /// </summary>
        /// <param name="origData"></param>
        /// <returns></returns>
        protected string strFixId(string origData)
        {
            return origData.Replace(".", "").Replace(" ", "_");
        }

        #endregion Protected Methods

        #region Private Methods

        /// <summary>
        /// Importa un file excel e restituisce una
        /// </summary>
        /// <param name="fileItem"></param>
        /// <returns></returns>
        private string processExcelImport(string fileItem, OutDataType outReq = OutDataType.RegGiacenze)
        {
            string outVal = "";
            switch (outReq)
            {
                case OutDataType.ParamTaglioUstd:
                    outVal = getParTaglioUstdJson(fileItem, true);
                    break;
                case OutDataType.RegGiacenze:
                default:
                    outVal = getRegGiacJson(fileItem);
                    break;
            }
            return outVal;
        }

        /// <summary>
        /// Processa e restituisce un oggetto lista giacenze serializzato
        /// </summary>
        /// <param name="fileItem">Nome file excel</param>
        /// <param name="doIndent">Output indentato</param>
        /// <returns></returns>
        private string getRegGiacJson(string fileItem, bool doIndent = false)
        {
            int numErr = 0;
            string outVal = "";
            // test procedura di import files excel (default Giacovelli...)
            var currExcel = new ExcelMan(fileItem);
            // creo lista dati in formato RegGiacenze...
            Dictionary<string, BatchRec> listaGiac = new Dictionary<string, BatchRec>();
            var dtSet = currExcel.getDataSet();
            if (dtSet != null && dtSet.Tables != null && dtSet.Tables.Count > 0)
            {
                string nomeFile = Path.GetFileName(fileItem);
                nomeFile = nomeFile.Substring(0, nomeFile.LastIndexOf("."));
                var elSheet = dtSet.Tables;
                int idxTab = 0;
                // cerco lo sheet corretto se > 1
                if (dtSet.Tables.Count > 1)
                {
                    bool found = false;
                    for (int i = 0; i < dtSet.Tables.Count; i++)
                    {
                        if (nomeFile.Contains(dtSet.Tables[i].TableName))
                        {
                            idxTab = i;
                            found = true;
                            break;
                        }
                        // controllo parametro opzionale...
                        if (!found && taskConfig != null && !string.IsNullOrEmpty(taskConfig.TargetName))
                        {
                            if (dtSet.Tables[i].TableName == taskConfig.TargetName)
                            {
                                idxTab = i;
                                break;
                            }
                        }
                    }
                }
                var tabella = dtSet.Tables[idxTab];
                int numRighe = tabella.Rows.Count;
                int idxODL = taskConfig != null ? taskConfig.IdxODL : 0;
                for (int i = 0; i < numRighe; i++)
                {
                    if (taskConfig != null && taskConfig.ProcessParamInt != null && taskConfig.ProcessParamInt.Count > 5)
                    {
                        if (numErr < numRighe / 5)
                        {
                            try
                            {
                                // variabili di appoggio...
                                DateTime dtRif = DateTime.Today;
                                double qtyTot = 0;
                                int numPack = 0;
                                var riga = tabella.Rows[i];
                                if (riga != null)
                                {
                                    string ddt = getCellVal(riga, taskConfig.ProcessParamInt["ExtDoc"]);
                                    string sDate = getCellVal(riga, taskConfig.ProcessParamInt["DateRif"]);
                                    string prod = getCellVal(riga, taskConfig.ProcessParamInt["Product"]);
                                    // verifiche x import: header, data e DDT (vuoti o "-") --> SKIP!
                                    bool checkHeaderKo = (ddt == "DDT" || prod == "PRODOTTO");
                                    bool checkEmptyDdt = (string.IsNullOrEmpty(ddt) || ddt == "-");
                                    bool checkEmptyDate = (string.IsNullOrEmpty(sDate) || sDate == "-");
                                    if (checkHeaderKo)
                                    {
                                        //lgTrace($"SKIP header");
                                    }
                                    else if (checkEmptyDdt || checkEmptyDate)
                                    {
                                        //lgTrace($"SKIP linea vuota | i: {i} | codice: {codice} | date: {serie} | prod: {prod}");
                                    }
                                    else
                                    {
                                        string variety = getCellVal(riga, taskConfig.ProcessParamInt["Variety"]);
                                        string suppl = getCellVal(riga, taskConfig.ProcessParamInt["Supplier"]);
                                        string sQty = getCellVal(riga, taskConfig.ProcessParamInt["QtyTot"]);
                                        string sNum = getCellVal(riga, taskConfig.ProcessParamInt["NumPack"]);
                                        string numPed = getCellVal(riga, taskConfig.ProcessParamInt["NumPed"]);
                                        string packPed = getCellVal(riga, taskConfig.ProcessParamInt["PackPed"]);
                                        string pesoPack = getCellVal(riga, taskConfig.ProcessParamInt["PesoPack"]);
                                        DateTime.TryParse(sDate, out dtRif);
                                        int.TryParse(sNum, out numPack);
                                        double.TryParse(sQty, out qtyTot);
                                        string identRG = ddt.Length > 2 ? $"{strFixId(ddt)}.{strFixId(prod)}.{strFixId(variety)}.{strFixId(suppl)}" : $"{dtRif:yyyyMMdd}.{strFixId(prod)}.{strFixId(variety)}.{strFixId(suppl)}";
                                        string notes = $"{numPed}x{packPed}x{pesoPack}";
                                        // verifico di avere dati per proseguire...
                                        bool checkIdent = !string.IsNullOrEmpty($"{prod}{variety}{suppl}");
                                        if (checkIdent)
                                        {
                                            BatchRec newRow = new BatchRec()
                                            {
                                                IdxODL = idxODL,
                                                IdentRG = identRG,
                                                DateRif = dtRif,
                                                ExtDoc = ddt,
                                                Product = prod,
                                                Variety = variety,
                                                Supplier = suppl,
                                                NumPack = numPack,
                                                QtyTot = qtyTot,
                                                Notes = notes
                                            };
                                            // verifico: se manca aggiungo
                                            if (!listaGiac.ContainsKey(identRG))
                                            {
                                                listaGiac.Add(identRG, newRow);
                                            }
                                            else
                                            {
                                                // altrimenti aggiorno giacenza con valori numerici
                                                listaGiac[identRG].NumPack += newRow.NumPack;
                                                listaGiac[identRG].QtyTot += newRow.QtyTot;
                                            }
                                        }
                                        else
                                        {
                                            //lgError($"Errore verifica identità riga | prod: {prod} | variety: {variety} | suppl: {suppl}");
                                            numErr++;
                                        }
                                    }
                                }
                            }
                            catch (Exception exc)
                            {
                                numErr++;
                            }
                        }
                    }
                }
            }
            if (listaGiac.Count > 0)
            {
                // converto in una nuova lista...
                int rCounter = 1;
                Dictionary<int, BatchRec> list2Send = new Dictionary<int, BatchRec>();
                foreach (var item in listaGiac)
                {
                    list2Send.Add(rCounter, item.Value);
                    rCounter++;
                }
                // serializzo e restituisco file JSON...
                Formatting fMode = doIndent ? Formatting.Indented : Formatting.None;
                var serVal = JsonConvert.SerializeObject(list2Send, fMode);
                if (serVal != null && !string.IsNullOrEmpty(serVal))
                {
                    outVal = serVal;
                }
            }
            return outVal;
        }

        /// <summary>
        /// Processa e restituisce un oggetto parametri taglio serializzato
        /// </summary>
        /// <param name="fileItem">Nome file excel</param>
        /// <param name="doIndent">Output indentato</param>
        /// <returns>Restituisce un serializzato formato Dictionary<string, CutterParam></returns>
        private string getParTaglioUstdJson(string fileItem, bool doIndent = false)
        {
            int numErr = 0;
            string outVal = "";
            // test procedura di import files excel (default Giacovelli...)
            var currExcel = new ExcelMan(fileItem);
            // creo lista dati in formato ParametriTaglioUSTD...
            Dictionary<string, CutterParam> listaParams = new Dictionary<string, CutterParam>();
            var dtSet = currExcel.getDataSet();
            if (dtSet != null && dtSet.Tables != null && dtSet.Tables.Count > 0)
            {
                string nomeFile = Path.GetFileName(fileItem);
                nomeFile = nomeFile.Substring(0, nomeFile.LastIndexOf("."));
                var elSheet = dtSet.Tables;
                int idxTab = 0;
                // cerco lo sheet corretto se > 1
                if (dtSet.Tables.Count > 1)
                {
                    bool found = false;
                    for (int i = 0; i < dtSet.Tables.Count; i++)
                    {
                        if (nomeFile.Contains(dtSet.Tables[i].TableName))
                        {
                            idxTab = i;
                            found = true;
                            break;
                        }
                        // controllo parametro opzionale...
                        if (!found && taskConfig != null && !string.IsNullOrEmpty(taskConfig.TargetName))
                        {
                            if (dtSet.Tables[i].TableName == taskConfig.TargetName)
                            {
                                idxTab = i;
                                break;
                            }
                        }
                    }
                }
                var tabella = dtSet.Tables[idxTab];
                int numRighe = tabella.Rows.Count;
                int idxODL = taskConfig != null ? taskConfig.IdxODL : 0;
                for (int i = 0; i < numRighe; i++)
                {
                    // voglio che ci sia setup x params
                    if (taskConfig != null && taskConfig.ProcessParamInt != null && taskConfig.ProcessParamInt.Count > 5)
                    {
                        // procedo se errori < 20%...
                        if (numErr < numRighe / 5)
                        {
                            try
                            {
                                double lungPezzo = 0;
                                double quotaUscita = 0;
                                double lungBarra = 0;
                                double altezza = 0;
                                var riga = tabella.Rows[i];
                                if (riga != null)
                                {
                                    string codice = getCellVal(riga, taskConfig.ProcessParamInt["Codice"]);
                                    string serie = getCellVal(riga, taskConfig.ProcessParamInt["Serie"]);
                                    string sLungPezzo = getCellVal(riga, taskConfig.ProcessParamInt["LungPezzo"]);
                                    string sQuotaUscita = getCellVal(riga, taskConfig.ProcessParamInt["QuotaUscita"]);
                                    string sLungBarra = getCellVal(riga, taskConfig.ProcessParamInt["LungBarra"]);
                                    string sAltezza = getCellVal(riga, taskConfig.ProcessParamInt["Altezza"]);

                                    // verifiche x import: header avrà codice e serie con nome "codice" e "serie" --> SKIP!
                                    bool chkHeaderKo = (codice.ToLower() == "codice" || serie.ToLower() == "serie");
                                    bool chkEmptyLP = (string.IsNullOrEmpty(sLungPezzo) || sLungPezzo.Length < 2);
                                    bool chkEmptyQU = (string.IsNullOrEmpty(sQuotaUscita) || sQuotaUscita.Length < 2);
                                    bool chkEmptyLB = (string.IsNullOrEmpty(sLungBarra) || sLungBarra.Length < 2);
                                    bool chkEmptyA = (string.IsNullOrEmpty(sAltezza) || sAltezza.Length < 2);
                                    if (chkHeaderKo)
                                    {
                                        //lgTrace($"SKIP header");
                                    }
                                    else if (chkEmptyLP || chkEmptyQU || chkEmptyLB || chkEmptyA)
                                    {
                                        //lgTrace($"SKIP linea vuota | i: {i} | codice: {codice} | date: {serie} | prod: {prod}");
                                    }
                                    else
                                    {
                                        // conversione valori double
                                        bool bLP = double.TryParse(sLungPezzo, out lungPezzo);
                                        bool bQU = double.TryParse(sQuotaUscita, out quotaUscita);
                                        bool bLB = double.TryParse(sLungBarra, out lungBarra);
                                        bool bA = double.TryParse(sAltezza, out altezza);

                                        // verifico di avere dati per proseguire...
                                        bool chkConvert = bLP && bQU && bLB && bA;
                                        if (chkConvert)
                                        {
                                            CutterParam newRow = new CutterParam()
                                            {
                                                Codice = codice,
                                                Serie = serie,
                                                LungPezzo = lungPezzo,
                                                QuotaUscita = quotaUscita,
                                                LungBarra = lungBarra,
                                                Altezza = altezza
                                            };
                                            // verifico: se manca aggiungo
                                            if (!listaParams.ContainsKey(codice))
                                            {
                                                listaParams.Add(codice, newRow);
                                            }
                                        }
                                        else
                                        {
                                            //lgError($"Errore verifica identità riga | prod: {prod} | variety: {variety} | suppl: {suppl}");
                                            numErr++;
                                        }
                                    }
                                }
                            }
                            catch (Exception exc)
                            {
                                numErr++;
                            }
                        }
                    }
                }
            }
            // serializzo direttamente la listaParams x velocizzare poi ricerca da dictionary
            if (listaParams.Count > 0)
            {
#if false
                // converto in una nuova lista...
                int rCounter = 1;
                Dictionary<int, CutterParam> list2Send = new Dictionary<int, CutterParam>();
                foreach (var item in listaParams)
                {
                    list2Send.Add(rCounter, item.Value);
                    rCounter++;
                }
                // serializzo e restituisco file JSON...
                Formatting fMode = doIndent ? Formatting.Indented : Formatting.None;
                var serVal = JsonConvert.SerializeObject(list2Send, fMode);
                if (serVal != null && !string.IsNullOrEmpty(serVal))
                {
                    outVal = serVal;
                } 
#endif
                // serializzo e restituisco file JSON...
                Formatting fMode = doIndent ? Formatting.Indented : Formatting.None;
                var serVal = JsonConvert.SerializeObject(listaParams, fMode);
                if (serVal != null && !string.IsNullOrEmpty(serVal))
                {
                    outVal = serVal;
                }
            }
            return outVal;
        }


        #endregion Private Methods
    }
}