using EvaPOS_API_FRAME.DTO.Intercambio;
using EvaPOS_API_FRAME.DTO;
using EvaPOS_API_FRAME.RespuestasXML;
using EvaPosSrvDTO;
using EvaPosSrvResp;
using Serilog;
using System.Globalization;
using RestSharp;
using gatewayGK.POSGk;
using System.Text.Json;
using GatewaySCO;
using Newtonsoft.Json;
using IO.Swagger.Model;
using static IO.Swagger.Model.ComGkSoftwarePosApiModelConfigProcessActionPositionOperationConfig;

namespace gatewayGK.ComandosGk
{
    ///<summary>
    /// Calcular el impuesto de un item.
    /// TODO - terminar esto.
    /// </summary>
    public class SubtotalCalculationCmdGk : IComando
    {
        public string Referencia { get; set; } = "scsns:AddItem"; // <-- Arreglar Referencia
        /// <summary>
        /// DTO con solicitud.
        /// </summary>
        public AddItemRequestDTO Request { get; private set; } // <-- Buscar el correcto.

        /// <summary>
        /// Url servicio autenticación Gk.
        /// </summary>
        private readonly string _urlRequest = Entorno<EntornoGK>.Instancia.get().UrlBase
            + "/com.gk_software.pos.api.service.subtotal.SubtotalService/processSubtotalCalculation";

        /// <summary>
        /// Calcular subtotal.
        /// </summary>
        public Respuestas Ejecutar()
        {
            Log.Debug("Cmd SubtotalCalculationCmdGk ejecutado. RequestID : {RequestID}", Request.RequestID);
            //int requestIdGuardado = RequestIdGuardado();
            POSBCStatusEvent posbcStatus = null;
            TransactionStatusEvent transactionStatusEvent = null;
            POSReceiptEventHeaderResponse posReceiptEventHeader = null;
            POSReceiptEvent pOSReceiptEvent = null;
            TotalsEventResponse totalEvent = null;

            // Objeto invocación servicio web, SubtotalServiceApi.cs
            ComGkSoftwarePosApiModelConfigProcessActionPositionOperationConfig configReq = new(
                false,  //(bool? forceQuantityInput = default(bool?)
                1000000000, //double? pricePositionLimit = default(double?)
                true,   //bool? pricePositionZeroAllowed = default(bool?)
                -1.0,   // double? priceTransactionLimit = default(double?)
                100.0,  //priceDifferencePercentPositionLimit = default(double?)
                99999999.0, // priceDifferenceAbsolutePositionLimit = default(double?)
                PriceDifferenceLimitExceedActionEnum.Authorize, //priceDifferenceLimitExceedAction = default(PriceDifferenceLimitExceedActionEnum)
                PriceNegDifferenceLimitExceedActionEnum.Unchecked, // PriceNegDifferenceLimitExceedActionEnum priceNegDifferenceLimitExceedAction = default(PriceNegDifferenceLimitExceedActionEnum), 
                CloseCurrentEnum.Disabled, // CloseCurrentEnum closeCurrent = default(CloseCurrentEnum), 
                ClosePreviousEnum.Required, // ClosePreviousEnum closePrevious = default(ClosePreviousEnum), 
                false, // bool? filterResult = default(bool?), 
                false, // bool? allowedWithWeight = default(bool?), 
                false, // bool? allowedWithLength = default(bool?), 
                false, // bool? allowedWithArea = default(bool?),
                false, // bool? allowedWithVolume = default(bool?), 
                false, // bool? allowedWithMeasure = default(bool?), 
                99999, // int? maximumQuantity = default(int?), 
                1, // int? minimumQuantity = default(int?),
                QuantityInputOrderEnum.BeforeOrAfter, // QuantityInputOrderEnum quantityInputOrder = default(QuantityInputOrderEnum),
                PriceInputOrderEnum.BeforeOrAfter, // PriceInputOrderEnum priceInputOrder = default(PriceInputOrderEnum), 
                QuantityLimitExceedActionEnum.Forbid, //  QuantityLimitExceedActionEnum quantityLimitExceedAction = default(QuantityLimitExceedActionEnum),
                AmountLimitExceedActionEnum.Forbid, // AmountLimitExceedActionEnum amountLimitExceedAction = default(AmountLimitExceedActionEnum), 
                0.0, //  double? weightUnitFactor = default(double?), 
                0.0, // double? measureUnitFactor = default(double?),
                PriceDeviationTypeEnum.All, // PriceDeviationTypeEnum priceDeviationType = default(PriceDeviationTypeEnum)
                PriceNegDeviationTypeEnum.All, // PriceNegDeviationTypeEnum priceNegDeviationType = default(PriceNegDeviationTypeEnum),
                "", "", "", "", "", "", "", "", "", "", "", "", "", "", "");
            // string xXCustom08 = default(string), string xXCustom10 = default(string), string xXCustom09 = default(string), string xXCustom07 = default(string), 
            // string xXCustom05 = default(string), string xXCustom06 = default(string), string xXCustom11 = default(string), string xXCustom12 = default(string), 
            // string xXCustom13 = default(string), string xXCustom14 = default(string), string xXCustom15 = default(string), string xXCustom03 = default(string), 
            // string xXCustom02 = default(string), string xXCustom04 = default(string), string xXCustom01 = default(string))

            // Objeto respuesta servicio web, SubtotalServiceApi.cs
            ComGkSoftwarePosApiServiceTransactionSubtotalOperationResult result;

            // Mensajes respuesta a CHEC.
            Respuestas respuesta = null;

            //Variables del adaptador de entrada - addItem
            int requestId = Request.RequestID;

            // --- cliente rest.
            var options = new RestClientOptions()
            {
                // TODO manejar el timeout como un parámetro de configuración.
                MaxTimeout = -1,
                RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true
            };
            string sessionId = Entorno<EntornoGK>.Instancia.get().posSessionId;
            string cookieValue = $"sessionid={sessionId}";

            var client = new RestClient(options);
            var request = new RestRequest(_urlRequest, Method.Post);
            request.AddHeader("Accept", "application/json;Format=GK-PLAIN-JSON");
            request.AddHeader("Content-Type", "application/json;Format=GK-PLAIN-JSON");
            request.AddHeader("_pos_session_", sessionId);
            request.AddHeader("Cookie", cookieValue);

            // -- Inicializar request. Nada que inicializar.
            string jsonReq = JsonConvert.SerializeObject(configReq);
            string body = $"{{ \"com.gk_software.pos.api.model.config.process.action.PositionOperationConfig\": {jsonReq} }}";

            request.AddJsonBody(body);

            Log.Verbose(">> GK processSubtotalCalculation - sessionId {sessionId}", sessionId);
            Log.Verbose(">> GK processSubtotalCalculation - body {body}", body);
            Log.Information(">> GK processSubtotalCalculation");

            RestResponse response = client.Execute(request);
            System.Net.HttpStatusCode statusCode = response.StatusCode;
            int numericStatusCode = (int)statusCode;

            Log.Information("<< GK processSubtotalCalculation - Http.StatusCode {StatusCode}, Http.numericStatusCode {numericStatusCode}", response.StatusCode, numericStatusCode);
            Log.Verbose("<< GK processSubtotalCalculation - Headers {Headers}", response.Headers);
            Log.Verbose("<< GK processSubtotalCalculation - Content {Content}", response.Content);

            if (response.IsSuccessful == false)
            {
                if (response.StatusCode == System.Net.HttpStatusCode.InternalServerError)
                {
                    var jsonRespErr = System.Text.Json.JsonSerializer.Deserialize<JsonElement>(response.Content
                        ?? throw new ApplicationException("Null content in response to POST method."));

                    var errorCode = jsonRespErr.GetProperty("errorCode").GetProperty("errorCode").GetString();
                    var message = jsonRespErr.GetProperty("errorCode").GetProperty("message").GetString();
                    var posItemID = jsonRespErr.GetProperty("additionalContextInfoMap").GetProperty("posItemID").GetString();
                    Log.Warning("<< GK processSubtotalCalculation - errorCode {errorCode}, message {message}, posItemID {posItemID}",
                            errorCode, message, posItemID);
                }
                Log.Error("<< GK processSubtotalCalculation failed: StatusCode {StatusCode}, Content {Content}",
                    response.StatusCode, response.Content);
                throw new ApplicationException("Servicio POST 'processSubtotalCalculation' en error.");
            }

            // --- En este punto, la respuesta es exitosa a nivel de http.

            result = Newtonsoft.Json.JsonConvert.DeserializeObject<ComGkSoftwarePosApiServiceTransactionSubtotalOperationResult>(
                    response.Content ?? throw new ApplicationException("Null content in response to POST method."))
                    ?? throw new ApplicationException("Null content in response to POST method.");

            // ------------------------------------------------------------------------------------
            // En este punto statusOk == True : el item ingreso a la transacción de venta.
            var transactionId = result.PrimaryEntry.Transaction.Key.TransactionID;
            var businessUnitGroupID = result.PrimaryEntry.Transaction.Key.BusinessUnitGroupID;
            var lineItemsList = result.PrimaryEntry.Transaction.RetailTransactionList[0].RetailTransactionLineItemList;

            // Recorrer lista 
            Log.Information("<< GK processSubtotalCalculation: transactionId {transactionId}", transactionId);
            Log.Information("-------------------------------------------------------------------------------------------------------------------------");
            Log.Information(String.Format("{0,3} | {1,-13} | {2,-20} | {3,-3}| {4,-5} | {5,-12} | {6,-12} | {7,-12} | {8,-12} | {9,3}",
                    "#",
                    "positemID",
                    "receiptText",
                    "und",
                    "qntty",
                    "actlUntPrice",
                    "extnddAmount",
                    "extnddDiscnt",
                    "grndExtdAmnt",
                    "tax"
                ));
            Log.Information("-------------------------------------------------------------------------------------------------------------------------");
            bool encabezadoTaxImpreso = false;
            foreach (var lineItem in lineItemsList)
            {
                //if (lineItem.SaleReturnLineItemList == null) continue;

                var retailTransactionLineItemTypeCode = lineItem.RetailTransactionLineItemTypeCode;
                Log.Debug("<< GK processSubtotalCalculation: transactionId {transactionId}, retailTransactionLineItemTypeCode {retailTransactionLineItemTypeCode}",
                        transactionId,
                        retailTransactionLineItemTypeCode
                    );

                switch (retailTransactionLineItemTypeCode)
                {
                    case "SR":
                        // transaction line item REGISTRO ITEM VENTA
                        var item = lineItem.SaleReturnLineItemList[0];
                        var positemID = item.PositemID;
                        var itemSequenceNumber = item.Key.RetailTransactionLineItemSequenceNumber;
                        var receiptText = item.ReceiptText;
                        var regularUnitPrice = item.RegularUnitPrice;
                        var actualUnitPrice = item.ActualUnitPrice;
                        var unitOfMeasureCode = item.UnitOfMeasureCode;
                        var quantity = item.Quantity;
                        var extendedAmount = item.ExtendedAmount;
                        var extendedDiscountAmount = item.ExtendedDiscountAmount;
                        var grandExtendedAmount = item.GrandExtendedAmount;
                        var taxLineItem = item.SaleReturnTaxLineItemList[0];
                        var taxReceiptPrintCode = taxLineItem.ReceiptPrintCode;

                        Log.Debug("<< GK processSubtotalCalculation: transactionId {transactionId}, itemId {itemId}, regularUnitPrice {regularUnitPrice}, quantity {quantity}, unitOfMeasureCode {unitOfMeasureCode}, receiptText {receiptText}",
                                transactionId,
                                positemID,
                                regularUnitPrice,
                                quantity,
                                unitOfMeasureCode,
                                receiptText
                                );

                        Log.Information(String.Format("{0,3} | {1,13} | {2,-20} | {3,3}| {4,-5:N2} | {5,12:N2} | {6,12:N2} | {7,12:N2} | {8,12:N2} | {9,3}",
                                itemSequenceNumber,
                                positemID,
                                receiptText,
                                unitOfMeasureCode,
                                quantity,
                                actualUnitPrice,
                                extendedAmount,
                                extendedDiscountAmount,
                                grandExtendedAmount,
                                taxReceiptPrintCode
                            ));
                        break;
                    case "TX":
                        // transaction line item REGISTRO ITEM IMPUESTO
                        var taxLineItemList = lineItem.TaxLineItemList;
                        if (encabezadoTaxImpreso == false)
                        {
                            encabezadoTaxImpreso = true;
                            Log.Information("-------------------------------------------------------------------------------------------------------------------------");
                            Log.Information(String.Format("{0,3} | {1,6} | {2,12} | {3,12} | {4,12} | {5,8}",
                                    "tax",
                                    "%",
                                    "taxAmount",
                                    "taxablAmount",
                                    "net",
                                    "taxName"
                                ));
                            Log.Information("-------------------------------------------------------------------------------------------------------------------------");
                        }
                        foreach (var taxLine in taxLineItemList)
                        {
                            var taxableAmount = taxLine.TaxableAmount;
                            var taxPercent = taxLine.TaxPercent;
                            var taxAmount = taxLine.TaxAmount;
                            var taxAuthorityName = taxLine.TaxAuthorityName;
                            var receiptPrintCode = taxLine.ReceiptPrintCode;
                            Log.Information(String.Format("{0,-3} | {1,6} | {2,12:N2} | {3,12:N2} | {4,12:N2} | {5,8}",
                                receiptPrintCode,
                                taxPercent,
                                taxAmount,
                                taxableAmount,
                                taxableAmount - taxAmount,
                                taxAuthorityName
                            ));
                        }
                        break;
                    default:
                        break;
                }
            }

            var retailTransactionList = result.PrimaryEntry.Transaction.RetailTransactionList;
            var dtoRetailTransactionTotalList = retailTransactionList[0].RetailTransactionTotalList;

            double? total = 0.0;
            double? subtotal = 0.0;
            double? subtotalDcto = 0.0;

            // Recorrer lista de totales y sacar valores
            foreach (var tipoTotal in dtoRetailTransactionTotalList)
            {
                var k = tipoTotal.Key;
                var valor = tipoTotal.Amount;
                switch (k.TransactionTotalTypeCode)
                {
                    case "TOTAL":
                        total = valor;
                        break;
                    case "SUBTOTAL":
                        subtotal = valor;
                        break;
                    case "SUBTOTAL_DISCOUNT":
                        subtotalDcto = valor;
                        break;
                    default:
                        break;
                        // throw new ApplicationException("Null content in response to POST method (TransactionTotalTypeCode).");
                }
            }

            Entorno<EntornoGK>.Instancia.get().TransactionID = transactionId;
            Log.Information("-------------------------------------------------------------------------------------------------------------------------");
            Log.Information(String.Format("\t\tSUBTOTAL: {0,12:N2} -- SUBTOTAL_DISCOUNT: {1,12:N2} -- TOTAL: {2,12:N2}",
                subtotal, subtotalDcto, total));
            Log.Information("-------------------------------------------------------------------------------------------------------------------------");

            //Valores que se mandan en chec
            var date = DateTime.Now.ToString("dd/MM/yyyy");
            CultureInfo culture = new CultureInfo("en-US");
            culture.DateTimeFormat.AMDesignator = "AM";
            culture.DateTimeFormat.PMDesignator = "PM";
            var time = DateTime.Now.ToString("hh:mm tt", culture);

            transactionStatusEvent = new TransactionStatusEvent(1, TipoMensaje.Event, requestId, "TRANSACTION_START", 1, "regularSale", "sales", date, time);
            pOSReceiptEvent = new POSReceiptEvent(1, TipoMensaje.Event, requestId, "Customer", 0, "Body", requestId, 1, "center", "LineItem", "ItemSale", "XXX", "Add");
            posReceiptEventHeader = new POSReceiptEventHeaderResponse(1, TipoMensaje.Event, requestId);
            totalEvent = new TotalsEventResponse(1, TipoMensaje.Event, requestId, "", "", "", "", "0.00", "0.00", "0.00", "0.00", "0.00", 1, 0, "0.00", "0.00");
            respuesta = new Respuestas { transactionStatusEvent, posReceiptEventHeader, pOSReceiptEvent, totalEvent };

            return respuesta;
        }
        public IComando CreaCopia()
        {
            return (AddItemRequestCmdGk)this.MemberwiseClone();
        }

        public void CargaDTO(DTOBase addItemRequestDTO)
        {
            Request = (AddItemRequestDTO)addItemRequestDTO;
        }
    }
}
