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;
using static IO.Swagger.Model.ComGkSoftwarePosApiServicePaymentTerminalPaymentRequest;

namespace gatewayGK.ComandosGk
{
    ///<summary>
    ///
    /// Actualiza autorización del tender con datos del pago.
    /// TODO teminar esto.
    /// </summary>
    public class UpdateTerminalTenderAuthorizationCmdGk : 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.payment.PaymentService/updateTerminalTenderAuthorization";

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

            // ComGkSoftwarePosApiServicePaymentTerminalPaymentRequest req = new(
            //     new ComGkSoftwarePosApiModelConfigProcessActionPositionOperationConfig(),  // ComGkSoftwarePosApiModelConfigProcessActionPositionOperationConfig positionOperationConfig = default(ComGkSoftwarePosApiModelConfigProcessActionPositionOperationConfig), 
            //     ComGkSoftwareGkrApiServerMdTenderDtoDomTenderDO tender = default(ComGkSoftwareGkrApiServerMdTenderDtoDomTenderDO), 
            //     ComGkSoftwarePosApiModelDomAppAmountAmount amount = default(ComGkSoftwarePosApiModelDomAppAmountAmount), 
            //     ComGkSoftwarePosApiModelDomAppAmountAmount tipAmount = default(ComGkSoftwarePosApiModelDomAppAmountAmount), 
            //     string baseIsoCurrencyCode = default(string), 
            //     string foreignIsoCurrencyCode = default(string),
            //     double? exchangeRate = default(double?), 
            //     bool? multiplyFlag = default(bool?), 
            //     string tenderClassCode = default(string), 
            //     string paymentDescription = default(string), 
            //     bool? keyedOnline = default(bool?), 
            //     bool? success = default(bool?), 
            //     bool? autoCancellation = default(bool?), 
            //     bool? cancellation = default(bool?), 
            //     string cardType = default(string), 
            //     string cardIssuerCode = default(string), 
            //     string accountNumber = default(string), 
            //     string cardEntryMethodCode = default(string), 
            //     double? cashbackAmount = default(double?), 
            //     string entrySourceCode = default(string), 
            //     string personalIDRequiredTypeCode = default(string), 
            //     int? personalIDReferenceNumber = default(int?), 
            //     string authorizationMethodCode = default(string), 
            //     string adjudicationCode = default(string), 
            //     string cardExpirationDate = default(string), 
            //     string cardHolderName = default(string), 
            //     string cardIssueSequenceNumber = default(string), 
            //     string cardStartDate = default(string), 
            //     string serviceCode = default(string), 
            //     string terminalTransactionID = default(string), 
            //     string terminalID = default(string), 
            //     int? terminalTransactionStatus = default(int?), 
            //     string terminalTransactionResult = default(string), 
            //     TerminalTransactionTypeCodeEnum? terminalTransactionTypeCode = default(TerminalTransactionTypeCodeEnum?), 
            //     string cardNumber = default(string), 
            //     string merchantPrintCache = default(string), 
            //     string customerPrintCache = default(string), 
            //     bool? merchantCachePrinted = default(bool?), 
            //     bool? customerCachePrinted = default(bool?), 
            //     string approvalCode = default(string), 
            //     string terminalType = default(string), 
            //     string merchantNumber = default(string), 
            //     string isoCurrencyNumber = default(string), 
            //     string currentDate = default(string), 
            //     int? transactionStatus = default(int?), 
            //     bool? forceOffline = default(bool?), 
            //     string cardHolderVerificationTypeCode = default(string), 
            //     bool? hostAuthorizedFlag = default(bool?), 
            //     int? traceNumber = default(int?), 
            //     int? terminalTransactionErrorNumber = default(int?), 
            //     string adjuctionText = default(string), 
            //     ComGkSoftwarePosApiModelDomAppServicesTransactionTenderAuthorizationKey currentTenderAuthorizationKey = default(ComGkSoftwarePosApiModelDomAppServicesTransactionTenderAuthorizationKey), 
            //     ComGkSoftwarePosFlowLibsPaymentTerminalTransactionRecoveryValues recoveryValues = default(ComGkSoftwarePosFlowLibsPaymentTerminalTransactionRecoveryValues), 
            //     RequestTypeEnum? requestType = default(RequestTypeEnum?), 
            //     int? origTenderAuthorizationNumber = default(int?), 
            //     int? voidingLineItemSequenceNumber = default(int?), 
            //     string terminalReferenceNumber = default(string), 
            //     string token = default(string), 
            //     string signatureFormat = default(string), 
            //     string signatureData = default(string), 
            //     string internationalBankAccountNumber = default(string), 
            //     string bankIdentifierCode = default(string), 
            //     string creditorID = default(string), 
            //     string mandateID = default(string), 
            //     string prenotificationText = default(string), 
            //     string terminalTenderDescription = default(string), 
            //     string applicationID = default(string), 
            //     string encryptedPAN = default(string), 
            //     string terminalTransactionCurrencyCode = default(string), 
            //     string purchaseRestriction = default(string), 
            //     string gradeRestriction = default(string), 
            //     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)
            // );

            ComGkSoftwarePosApiServicePaymentTerminalPaymentRequest req = new(
                exchangeRate: 1.0,
                multiplyFlag: false,
                success: true,
                autoCancellation: false,
                cancellation: false,
                merchantCachePrinted: false,
                customerCachePrinted: false,
                cardType: "306",
                accountNumber: "575712*********2489",
                cardEntryMethodCode: "MC",
                authorizationMethodCode: "NONE",
                cardExpirationDate: "",
                cardHolderName: "/",
                cardStartDate: "null"
            );

            // Inicializar objetos de invocación
            ComGkSoftwarePosApiModelConfigProcessActionPositionOperationConfig positionOperationConfig = new(
                forceQuantityInput: false,
                pricePositionLimit: 10000000.0, // TODO - validar
                pricePositionZeroAllowed: false,
                priceTransactionLimit: 10000000.0, // TODO - validar
                priceDifferencePercentPositionLimit: 100.0, // TODO - validar
                priceDifferenceAbsolutePositionLimit: 99999999.0, // TODO - validar
                priceDifferenceLimitExceedAction: PriceDifferenceLimitExceedActionEnum.Authorize,
                priceNegDifferenceLimitExceedAction: PriceNegDifferenceLimitExceedActionEnum.Unchecked,
                closeCurrent: CloseCurrentEnum.Required,
                closePrevious: ClosePreviousEnum.Required,
                filterResult: true,
                allowedWithWeight: false,
                allowedWithLength: false,
                allowedWithArea: false,
                allowedWithVolume: false,
                allowedWithMeasure: false,
                maximumQuantity: 99999, // TODO - validar
                quantityInputOrder: QuantityInputOrderEnum.BeforeOrAfter,
                priceInputOrder: PriceInputOrderEnum.BeforeOrAfter,
                quantityLimitExceedAction: QuantityLimitExceedActionEnum.Forbid,
                priceDeviationType: PriceDeviationTypeEnum.All,
                priceNegDeviationType: PriceNegDeviationTypeEnum.All
            );

            ComGkSoftwareGkrApiServerMdCurrencyDtoDomCurrencyDOKey currencyKey = new(
                 businessUnitGroupID: Entorno<EntornoGK>.Instancia.get().BusinessUnitGroupID,
                 isocurrencyCode: "COP"
             );

            ComGkSoftwarePosApiModelDomAppAmountAmount amount = new(
                10.55, // <-- TODO : PONER EL VALOR ADECUADO.
                new ComGkSoftwareGkrApiServerMdCurrencyDtoDomCurrencyDO(currencyKey)
            );

            ComGkSoftwareGkrApiServerMdTenderDtoDomTenderDOKey tenderKey = new(
                businessUnitGroupID: Entorno<EntornoGK>.Instancia.get().BusinessUnitGroupID,
                tenderTypeCode: "ZTTE"
            );

            ComGkSoftwareGkrApiServerMdTenderDtoDomTenderDO tender = new(
                key: tenderKey,
                tenderClassCode: "TE",
                isocurrencyCode: currencyKey.IsocurrencyCode,
                minimumAcceptAmount: 0.0,
                maximumAcceptAmount: 10000000.0
            );

            ComGkSoftwarePosApiModelDomAppServicesTransactionTenderAuthorizationKey tenderAuthorizationKey = new(
                businessUnitGroupID: Entorno<EntornoGK>.Instancia.get().BusinessUnitGroupID,
                transactionID: "", //  TODO - fijar el correcto.
                tenderAuthorizationSequenceNumber: 0
            );

            req.PositionOperationConfig = positionOperationConfig;
            req.Amount = amount;
            req.BaseIsoCurrencyCode = currencyKey.IsocurrencyCode;
            req.Tender = tender;
            req.CurrentTenderAuthorizationKey = tenderAuthorizationKey;
            req.RequestType = RequestTypeEnum.CREATETENDERAUTHORIZATION;

            Log.Debug("Cmd UpdateTerminalTenderAuthorizationCmdGk ComGkSoftwarePosApiServicePaymentTerminalPaymentRequest {req}", req);

            // Objeto respuesta servicio web
            ComGkSoftwarePosApiServiceTransactionTransactionOperationResult 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(req);
            //string body = $"{{ \"com.gk_software.pos.api.model.config.process.action.PositionOperationConfig\": {jsonReq} }}";

            request.AddJsonBody(jsonReq);

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

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

            Log.Information("<< GK paymentTerminalPaymentRequest - Http.StatusCode {StatusCode}, Http.numericStatusCode {numericStatusCode}", response.StatusCode, numericStatusCode);
            Log.Verbose("<< GK paymentTerminalPaymentRequest - Headers {Headers}", response.Headers);
            Log.Verbose("<< GK paymentTerminalPaymentRequest - 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();
                    Log.Warning("<< GK paymentTerminalPaymentRequest - errorCode {errorCode}, message {message}",
                            errorCode, message);
                }
                Log.Error("<< GK paymentTerminalPaymentRequest failed: StatusCode {StatusCode}, Content {Content}",
                    response.StatusCode, response.Content);
                throw new ApplicationException("Servicio POST 'paymentTerminalPaymentRequest' en error.");
            }

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

            result = Newtonsoft.Json.JsonConvert.DeserializeObject<ComGkSoftwarePosApiServiceTransactionTransactionOperationResult>(
                    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 paymentTerminalPaymentRequest: 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 paymentTerminalPaymentRequest: 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 paymentTerminalPaymentRequest: 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;
        }
    }
}
