using AppData;
using Newtonsoft.Json;
using NKC_SDK;
using SteamWare;
using System;
using System.IO;
using System.Web.Http;

namespace NKC_WF.Controllers
{
    public class BatchProcController : ApiController
    {
        #region Protected Fields

        /// <summary>
        /// oggetto static/singleton per fare chiamate sul datalayer
        /// </summary>
        protected DataLayer DLMan = new DataLayer();

        #endregion Protected Fields

        #region Public Methods

        /// <summary>
        /// Restituisce SE C'E' la richiesta di elaborazione BATCH corrente
        /// </summary>
        /// <returns></returns>
        // GET: api/BatchProc
        [HttpGet]
        public batchRequest Get()
        {
            batchRequest answ = new batchRequest();
            string redKey = "";
            string redVal = "";
            // in primis: controllo su redis SE HO una richiesta CURRENT di processing...
            string envNum = ComLib.currBatchReq;
            // se c'è carico "la busta" come ID
            if (!string.IsNullOrEmpty(envNum))
            {
                // cerco in REDIS se ci sia l'item richiesto
                redKey = $"{ComLib.redOutPath}:{envNum}";
                // leggo da redis la stringa...
                redVal = memLayer.ML.getRSV(redKey);
                // PROVO a deserializzare...
                try
                {
                    answ = JsonConvert.DeserializeObject<batchRequest>(redVal);
                    // aggiungo LA SUA ENV NUM!!!!
                    answ.EnvNum = envNum;
                }
                catch
                { }
            }
            // altrimenti NULL!
            else
            {
                answ = null;
            }
            return answ;
        }

        /// <summary>
        /// Restituisce UNA SPECIFICA richiesta di elaborazione BATCH
        /// </summary>
        /// <returns></returns>
        // GET: api/BatchProc/5
        [HttpGet]
        public batchRequest Get(int id)
        {
            batchRequest answ = new batchRequest();
            // in primis: controllo su redis SE HO una richiista CURRENT di processing...
            string redKey = $"{ComLib.redOutPath}:{id}";
            string redVal = memLayer.ML.getRSV(redKey);
            // se c'è carico "la busta" come ID
            if (!string.IsNullOrEmpty(redVal))
            {
                // PROVO a deserializzare...
                try
                {
                    answ = JsonConvert.DeserializeObject<batchRequest>(redVal);
                }
                catch
                { }
            }
            return answ;
        }

        /// <summary>
        /// Processa una chiamata POST per l'invio in blocco Batch
        /// POST: api/BatchProc
        /// </summary>
        /// <param name="id">ID dell'IOB</param>
        /// <returns></returns>
        // POST: api/BatchProc
        [HttpPost]
        public string Post()
        {
            string answ = "";
            bool isSplitReq = false;
            // questa classe è derivata da Controller.Response... x cui recupero lo stream in altro modo...
            string content = "";
            System.Web.HttpContext.Current.Request.InputStream.Position = 0;
            using (var reader = new StreamReader(System.Web.HttpContext.Current.Request.InputStream, System.Text.Encoding.UTF8, true, 4096, true))
            {
                content = reader.ReadToEnd();
            }
            //Rest
            System.Web.HttpContext.Current.Request.InputStream.Position = 0;
            // procedo a deserializzare in blocco l'oggetto...
            try
            {
                // DEBUG: salvo su redis x fare DEBUG
                string redKey = $"{ComLib.redNestAnsw}:LAST_CALL";
                memLayer.ML.setRSV(redKey, content);

                // deserializzo
                baseNestAnsw batchProcAnsw = JsonConvert.DeserializeObject<baseNestAnsw>(content);
                // procedura idempotente: elimino eventuali dati della "busta" precedente...
                DLMan.taEL.deteteByParent("", batchProcAnsw.EnvNum);
                // se ho errori inizio a salvarli...
                if (batchProcAnsw.ErrorList != null && batchProcAnsw.ErrorList.Count > 0)
                {
                    // ora insert
                    foreach (var item in batchProcAnsw.ErrorList)
                    {
                        // salvo log errore...
                        DLMan.taEL.insertQuery(DateTime.Now, item.ErrType, $"{batchProcAnsw.EnvNum}", $"{item.Uid}", $"{item.Description}");
                    }
                }
                // se non nullo...
                if (batchProcAnsw != null)
                {
                    /*************************************************
                     * IN BASE al tipo di risposta saprò se
                     * - è BatchReq / OfflineOrder
                     * - è stima iniziale o NEST dettagliato
                     * - si tratta di una stima di validazione ITEM ...
                     *
                     *************************************************/
                    if (batchProcAnsw.OrderType == oType.BatchRequest)
                    {
                        // stima "classica"
                        if (batchProcAnsw.ProcType == 1)
                        {
                            int bStatus = 0;
                            // deserializzo come BatchreqIniziale (stima)
                            nestReplyBatchInitial rispStima = JsonConvert.DeserializeObject<nestReplyBatchInitial>(content);

                            // 2020.01.16 salvo su mongoDb la risposta...
                            ComLib.man.saveEstAnsw(rispStima);

                            // recupero info sul batch / KIT specifico x capire se sia di tipo "validation"
                            bool isValidation = false;
                            bool isTesting = false;
                            var tabOrd = DLMan.taOL.getByBatch(rispStima.BatchID);
                            if (tabOrd.Count > 0)
                            {
                                isValidation = tabOrd[0].OrdType == "V";
                                isTesting = tabOrd[0].OrdType == "T";
                            }
                            // calcolo status del batch...
                            switch (rispStima.ProcessStatus)
                            {
                                case procStatus.waiting:
                                case procStatus.running:
                                    bStatus = 1;
                                    break;

                                case procStatus.completed:
                                    if (isValidation || isTesting)
                                    {
                                        bool pdfOk = true;
                                        // se richiesto CheckPDF
                                        if (memLayer.ML.CRB("checkPdfPathTV"))
                                        {
                                            // verifico PDF, se NON OK --> errore
                                            string pdfPath = "";
                                            foreach (var item in rispStima.PartList)
                                            {
                                                pdfOk = pdfOk && ComLib.checkPdfExistAccessible(item, out pdfPath);
                                                if (!pdfOk)
                                                {
                                                    // codice è B.xxx dove xxx = BatchID
                                                    DLMan.taEL.insertQuery(DateTime.Now, "Check PDF path", $"B.{rispStima.BatchID}", $"{rispStima.BatchID}.{item.PartExtCode}", $"Error: PDF file not found: {pdfPath}");
                                                }
                                            }
                                        }

                                        // se non OK --> registro errore...
                                        if (!pdfOk)
                                        {
                                            bStatus = 9;
                                        }
                                        // verifico se il tempo di procesisng stimato sia > minimo...
                                        else if (rispStima.EstimatedWorktime > memLayer.ML.CRI("minValidEstSec"))
                                        {
                                            bStatus = 10;
                                        }
                                        else
                                        {
                                            bStatus = 9;
                                        }
                                    }
                                    else
                                    {
                                        bStatus = 2;
                                    }
                                    break;

                                case procStatus.accepted:
                                    bStatus = 5;
                                    break;

                                case procStatus.refused:
                                    if (isValidation || isTesting)
                                    {
                                        bStatus = 9;
                                    }
                                    else
                                    {
                                        bStatus = 6;
                                    }
                                    break;

                                case procStatus.error:
                                    if (isValidation || isTesting)
                                    {
                                        bStatus = 9;
                                    }
                                    else
                                    {
                                        bStatus = 7;
                                    }
                                    break;

                                case procStatus.aborted:
                                default:
                                    if (isValidation || isTesting)
                                    {
                                        bStatus = 9;
                                    }
                                    else
                                    {
                                        bStatus = 0;
                                    }
                                    break;
                            }
                            // SALVO info riguardo al batch running
                            DLMan.taBL.updateStatus(rispStima.BatchID, bStatus, rispStima.EnvNum, (decimal)rispStima.EstimatedWorktime / 60);
                            // salvo update elenco ITEMS
                            ComLib.updatePartsFromNesting(rispStima.PartList);
                            // aggiorno cadPath x items che non abbiano valorizzato...
                            string dxfFolder = memLayer.ML.CRS("drawingFolder");
                            if (isTesting)
                            {
                                dxfFolder = memLayer.ML.CRS("fileTestFolder");
                            }
                            string cadBasePath = $"{memLayer.ML.CRS("nestBasePath")}{dxfFolder}/";
                            DLMan.taIL.updateCadPath(cadBasePath, rispStima.BatchID, false);
                            // verifico IN CASO di validazione andata a buon fine --> valorizzo tabella!
                            if (bStatus > 7)
                            {
                                if (isValidation)
                                {
                                    // recupero ordine da batch
                                    if (tabOrd.Count > 0)
                                    {
                                        DLMan.taIV.upsertQuery(tabOrd[0].OrderExtCode, rispStima.BatchID, bStatus, rispStima.EstimatedWorktime);
                                    }
                                }
                                else if (isTesting)
                                {
                                    // recupero ordine da batch
                                    if (tabOrd.Count > 0)
                                    {
                                        DLMan.taFV.upsertQuery(tabOrd[0].OrderExtCode, rispStima.BatchID, bStatus, rispStima.EstimatedWorktime);
                                    }
                                }
                            }
                            // registro OK
                            answ = "OK";
                        }
                        // stima "extended" x splitting
                        else if (batchProcAnsw.ProcType == 3)
                        {
                            int bStatus = 0;
                            // deserializzo come BatchreqIniziale (stima)
                            nestReplyBatchExtEst rispStima = JsonConvert.DeserializeObject<nestReplyBatchExtEst>(content);

                            // salvo su mongoDb la risposta...
                            ComLib.man.saveExtEstAnsw(rispStima);

                            // recupero info sul batch / KIT specifico x capire se sia di tipo "validation"
                            bool isValidation = false;
                            bool isTesting = false;
                            var tabOrd = DLMan.taOL.getByBatch(rispStima.BatchID);
                            if (tabOrd.Count > 0)
                            {
                                isValidation = tabOrd[0].OrdType == "V";
                                isTesting = tabOrd[0].OrdType == "T";
                            }
                            // calcolo status del batch...
                            switch (rispStima.ProcessStatus)
                            {
                                case procStatus.waiting:
                                case procStatus.running:
                                    bStatus = 1;
                                    break;

                                case procStatus.completed:
                                    if (isValidation || isTesting)
                                    {
                                        bool pdfOk = true;
                                        // se richiesto CheckPDF
                                        if (memLayer.ML.CRB("checkPdfPathTV"))
                                        {
                                            // verifico PDF, se NON OK --> errore
                                            string pdfPath = "";
                                            foreach (var item in rispStima.PartList)
                                            {
                                                pdfOk = pdfOk && ComLib.checkPdfExistAccessible(item, out pdfPath);
                                                if (!pdfOk)
                                                {
                                                    // codice è B.xxx dove xxx = BatchID
                                                    DLMan.taEL.insertQuery(DateTime.Now, "Check PDF path", $"B.{rispStima.BatchID}", $"{rispStima.BatchID}.{item.PartExtCode}", $"Error: PDF file not found: {pdfPath}");
                                                }
                                            }
                                        }

                                        // se non OK --> registro errore...
                                        if (!pdfOk)
                                        {
                                            bStatus = 9;
                                        }
                                        // verifico se il tempo di procesisng stimato sia > minimo...
                                        else if (rispStima.EstimatedWorktime > memLayer.ML.CRI("minValidEstSec"))
                                        {
                                            bStatus = 10;
                                        }
                                        else
                                        {
                                            bStatus = 9;
                                        }
                                    }
                                    else
                                    {
                                        bStatus = 2;
                                    }
                                    break;

                                case procStatus.accepted:
                                    bStatus = 5;
                                    break;

                                case procStatus.refused:
                                    if (isValidation || isTesting)
                                    {
                                        bStatus = 9;
                                    }
                                    else
                                    {
                                        bStatus = 6;
                                    }
                                    break;

                                case procStatus.error:
                                    if (isValidation || isTesting)
                                    {
                                        bStatus = 9;
                                    }
                                    else
                                    {
                                        bStatus = 7;
                                    }
                                    break;

                                case procStatus.aborted:
                                default:
                                    if (isValidation || isTesting)
                                    {
                                        bStatus = 9;
                                    }
                                    else
                                    {
                                        bStatus = 0;
                                    }
                                    break;
                            }
                            // SALVO info riguardo al batch running
                            DLMan.taBL.updateStatus(rispStima.BatchID, bStatus, rispStima.EnvNum, (decimal)rispStima.EstimatedWorktime / 60);
                            // salvo update elenco ITEMS
                            ComLib.updatePartsFromNesting(rispStima.PartList);

                            // aggiorno la risposta dei tempi di esecuzione (+ NUM cart/part) degli ordini x permettere aggiustamenti
                            ComLib.updateExtEstimFromNesting(rispStima.BatchID, rispStima.EstOrderList);

                            // aggiorno cadPath x items che non abbiano valorizzato...
                            string dxfFolder = memLayer.ML.CRS("drawingFolder");
                            if (isTesting)
                            {
                                dxfFolder = memLayer.ML.CRS("fileTestFolder");
                            }
                            string cadBasePath = $"{memLayer.ML.CRS("nestBasePath")}{dxfFolder}/";
                            DLMan.taIL.updateCadPath(cadBasePath, rispStima.BatchID, false);
                            // registro OK
                            answ = "OK";
                        }
                        // nesting
                        else if (batchProcAnsw.ProcType == 2)
                        {
                            // deserializzo come BatchreqFinale
                            nestReplyBatchFinal rispNest = JsonConvert.DeserializeObject<nestReplyBatchFinal>(content);

                            // 2020.01.16 salvo su mongoDb la risposta...
                            ComLib.man.saveNestAnsw(rispNest);

                            // calcolo status del batch...
                            int bStatus = 2;
                            switch (rispNest.ProcessStatus)
                            {
                                case procStatus.waiting:
                                case procStatus.running:
                                    bStatus = 3;
                                    break;

                                case procStatus.completed:
                                    bStatus = 4;
                                    break;

                                case procStatus.accepted:
                                    bStatus = 5;
                                    break;

                                case procStatus.refused:
                                    bStatus = 6;
                                    break;

                                case procStatus.error:
                                    bStatus = 7;
                                    break;

                                case procStatus.aborted:
                                default:
                                    bStatus = 2;
                                    break;
                            }
                            // aggiorno il resto SOLO SE status == completo...
                            if (rispNest.ProcessStatus == procStatus.completed || rispNest.ProcessStatus == procStatus.error)
                            {
                                // resetto le precedenti elaborazioni: elimino dati child MA NON il batch...
                                DLMan.taBL.resetTree(rispNest.BatchID);
                                // SALVO info riguardo al batch completato
                                DLMan.taBL.updateStatus(rispNest.BatchID, bStatus, rispNest.EnvNum, (decimal)rispNest.EstimatedWorktime / 60);
                                // salvo info riguardo ai vari Bunk / Sheets / Items...
                                ComLib.updateBunksFromNesting(rispNest.BatchID, rispNest.BunkList);
                                // salvo info x CART & BINS previsti
                                ComLib.updateBinsFromNesting(rispNest.BatchID, rispNest.BinList);
                                ComLib.updateCartsFromNesting(rispNest.BatchID, rispNest.CartList);

                                // NKC2: se è un batch ti dipo descendant
                                if (ComLib.BType(rispNest.BatchID) == BatchType.Descendant)
                                {
                                    // verifico se ce ne siano altri NON validati (ma splitted)
                                    var tabDesc = ComLib.BatchOtherDescendant(rispNest.BatchID);
                                    if (tabDesc != null && tabDesc.Count > 0)
                                    {
                                        // ciclo x tutte le righe che NON fossero con nesting effettuato
                                        foreach (var item in tabDesc)
                                        {
                                            // se c'è qualcosa da processare lo richiede
                                            if (item.STATUS == 2)
                                            {
                                                // invio il PRIMO batch descendant
                                                ComLib.sendBatchReq(item.BatchID, "Nesting", 2, false);
                                                // registro su DB nesting iniziato...
                                                DLMan.taBL.updateStatus(item.BatchID, (int)BatchStatus.NestRequested, "", -1);
                                                isSplitReq = true;
                                                // esco
                                                break;
                                            }
                                            // se non ha trovato nulla --> prova update Batch Ancestor...
                                        }
                                    }
                                }
                            }

                            // registro OK
                            answ = "OK";
                        }
                    }
                    else if (batchProcAnsw.OrderType == oType.OfflineOrder)
                    {
                        // deserializzo come OfflineOrder
                        nestReplyOffOrd rispOffOrd = JsonConvert.DeserializeObject<nestReplyOffOrd>(content);

                        // 2020.8.18 salvo su mongoDb la risposta...
                        ComLib.man.saveOffOrdAnsw(rispOffOrd);

                        // serisposta non nulla...
                        if (rispOffOrd != null)
                        {
                            // poiché passo valore negativo scarto batch positivi..
                            if (rispOffOrd.BatchID > 0)
                            {
                                answ = "WRONG DATA (expected negative BatchID)";
                            }
                            else
                            {
                                // verifica PRELIMINARE se fosse in stato errore...
                                if (rispOffOrd.ProcessStatus == procStatus.error)
                                {
                                    // status -1 --> ERRORE!!!
                                    DLMan.taOffOL.updateStatus(Math.Abs(rispOffOrd.BatchID), -1);
                                }
                                else
                                {
                                    if (string.IsNullOrEmpty(rispOffOrd.DrawingPath))
                                    {
                                        answ = "WRONG DATA (expected DrawingPath not null)";
                                    }
                                    else
                                    {
                                        string nestBasePath = memLayer.ML.CRS("nestBasePath").ToLower();
                                        string servBasePath = memLayer.ML.CRS("servBasePath").ToLower();
                                        string fixPathDraw = rispOffOrd.DrawingPath.ToLower().Replace(nestBasePath, servBasePath).Replace('/', '\\');
                                        string fixPathCnc = rispOffOrd.CncPath.ToLower().Replace(nestBasePath, servBasePath).Replace('/', '\\');
                                        // segno offline order come processato registrando il disegno e segno ogni PART come lavorata da OffOrd2Item (status 992)
                                        DLMan.taOffOL.updateDrawing(Math.Abs(rispOffOrd.BatchID), fixPathDraw, fixPathCnc);
                                    }
                                }
                            }
                        }
                        // SALVO!!!
                        answ = "OK";
                    }
                }
                else
                {
                    answ = "WRONG DATA (expected baseNestAnsw object)";
                }
            }
            catch (Exception exc)
            {
                answ = "NO";
            }
            // se tutto OK --> tolgo ultimo batch
            if (answ == "OK")
            {
                // se è una split req --> NON resetto...
                if (!isSplitReq)
                {
                    // invio notifica che c'è una busta da processare
                    bool resetOk = ComLib.resetBatchReq();
                    // se tutto ok e ci sono da validare parts --> procedo!
                    if (resetOk)
                    {
                        bool nextValidSent = ComLib.sendFirstValidationBatch();
                    }
                }
            }
            // restituisco risposta
            return answ;
        }

        #endregion Public Methods
    }
}