const pairing = {
    pair: (spi) => {
        spi.Pair();
    },
    unpair: (spi) => {
        spi.Unpair();
        window.localStorage.removeItem("api_key");
        window.localStorage.removeItem("eftpos_address");
        window.localStorage.removeItem("posID");
        window.localStorage.removeItem("serial");
        window.localStorage.removeItem("test_mode");
        window.localStorage.removeItem("auto_address");
        window.localStorage.removeItem("use_secure_web_sockets");
        window.localStorage.removeItem("secrets");
        window.localStorage.removeItem("check-sig-eftpos");
    },
    pairingCancel: (spi) => {
        spi.PairingCancel();
    },
    pairingConfirmCode: (spi) => {
        spi.PairingConfirmCode();
    },
    setAcquirerCode: (spi, acquirerCode) => {
        spi.SetAcquirerCode(acquirerCode);
    },
    setUseSecureWebSockets: (spi, isSecure) => {
        spi.SetSecureWebSockets(isSecure);
    },
    saveSettings: async (
        spi,
        posId,
        testMode,
        serial,
        apiKey,
        secureWebSocket,
        autoAddress,
        eftpos,
        signatureFlowOnEftpos
    ) => {
        if (autoAddress != null) {
            spi.SetAutoAddressResolution(autoAddress);
        }
        spi.SetTestMode(testMode);
        spi.SetSecureWebSockets(secureWebSocket);
        spi.SetPosId(posId);
        spi.SetSerialNumber(serial);
        spi.SetDeviceApiKey(apiKey);
        spi.SetEftposAddress(eftpos);
        spi.Config.SignatureFlowOnEftpos = signatureFlowOnEftpos;

        window.localStorage.setItem("api_key", apiKey);
        window.localStorage.setItem("eftpos_address", eftpos);
        window.localStorage.setItem("posID", posId);
        window.localStorage.setItem("serial", serial);
        window.localStorage.setItem("test_mode", testMode.toString());
        window.localStorage.setItem("auto_address", autoAddress.toString());
        //spi service 的start方法会从 local storage中读取check-sig-eftpos，赋值给 spi.Config.SignatureFlowOnEftpos
        window.localStorage.setItem("check-sig-eftpos", signatureFlowOnEftpos);

        window.localStorage.setItem(
            "use_secure_web_sockets",
            secureWebSocket.toString()
        );
    },
    handlePairingStatusUpdate: (pairingState, logger) => {
        logger.Info(`### PAIRING PROCESS UPDATE ###`);
        logger.Info(`# Process: ${pairingState.Message}`);
        logger.Info(`# Confirmation Code: ${pairingState.ConfirmationCode}`);
        logger.Info(`# Finished? ${pairingState.Finished}`);
        logger.Info(`# Successful? ${pairingState.Successful}`);
        logger.Info(
            `# Waiting Confirm from Eftpos? ${pairingState.AwaitingCheckFromEftpos}`
        );
        logger.Info(
            `# Waiting Confirm from POS? ${pairingState.AwaitingCheckFromPos}`
        );
    },
};

const purchase = {
    initiatePurchase: (
        spi,
        options,
        posRefId,
        purchaseAmount,
        tipAmount,
        cashoutAmount,
        surchargeAmount,
        promptForCashout
    ) => {
        const res = spi.InitiatePurchaseTxV2(
            posRefId,
            purchaseAmount,
            tipAmount,
            cashoutAmount,
            promptForCashout,
            options,
            surchargeAmount
        );
        return res;
    },

    handleSuccessfulTransaction: (txStateResponse, logger) => {
        const purchaseResponse = new PurchaseResponse(txStateResponse);
        logger.Info(`# WOOHOO - WE GOT PAID!`);
        logger.Info(
            `# POS Reference: ${purchaseResponse.GetResponseValue("pos_ref_id")}`
        );
        logger.Info(`# Response: ${purchaseResponse.GetResponseText()}`);
        logger.Info(`# RRN: ${purchaseResponse.GetRRN()}`);
        logger.Info(`# Scheme: ${purchaseResponse.SchemeName}`);
        logger.Info(`# Customer Receipt:`);
        logger.Info(
            !purchaseResponse.WasCustomerReceiptPrinted()
                ? purchaseResponse.GetCustomerReceipt().trim()
                : `# PRINTED FROM EFTPOS`
        );
        logger.Info(`# PURCHASE: ${purchaseResponse.GetPurchaseAmount()}`);
        logger.Info(`# TIP: ${purchaseResponse.GetTipAmount()}`);
        logger.Info(`# SURCHARGE: ${purchaseResponse.GetSurchargeAmount()}`);
        logger.Info(`# CASHOUT: ${purchaseResponse.GetCashoutAmount()}`);
        logger.Info(
            `# BANKED NON-CASH AMOUNT: ${purchaseResponse.GetBankNonCashAmount()}`
        );
        logger.Info(
            `# BANKED CASH AMOUNT: ${purchaseResponse.GetBankCashAmount()}`
        );
    },
    handleFailedTransaction: (txStateResponse, logger) => {
        logger.Info(`# WE DID NOT GET PAID :(`);
        if (txStateResponse != null) {
            const purchaseResponse = new PurchaseResponse(txStateResponse);
            logger.Info(`# Error: ${txStateResponse.GetError()}`);
            logger.Info(`# Error Detail: ${txStateResponse.GetErrorDetail()}`);
            logger.Info(`# Response: ${purchaseResponse.GetResponseText()}`);
            logger.Info(`# RRN: ${purchaseResponse.GetRRN()}`);
            logger.Info(`# Scheme: ${purchaseResponse.SchemeName}`);
            logger.Info(`# Customer Receipt:`);
            logger.Info(
                !purchaseResponse.WasCustomerReceiptPrinted()
                    ? purchaseResponse.GetCustomerReceipt().trim()
                    : `# PRINTED FROM EFTPOS`
            );
        }
    },
    handleUnknownState: (logger) => {
        logger.Info(`# WE'RE NOT QUITE SURE WHETHER WE GOT PAID OR NOT :/`);
        logger.Info(
            `# CHECK THE LAST TRANSACTION ON THE EFTPOS ITSELF FROM THE APPROPRIATE MENU ITEM.`
        );
        logger.Info(`# IF YOU CONFIRM THAT THE CUSTOMER PAID, CLOSE THE ORDER.`);
        logger.Info(`# OTHERWISE, RETRY THE PAYMENT FROM SCRATCH.`);
    },
};

const refund = {
    initiateRefund(spi, posRefId, refundAmount, isMerchantPasswordSuppressed) {
        const res = spi.InitiateRefundTx(
            posRefId,
            refundAmount,
            isMerchantPasswordSuppressed
        );
        return res;
    },
    handleFailedTransaction(txStateResponse, logger) {
        logger.Info(`# REFUND FAILED!`);
        if (txStateResponse !== null) {
            const refundResponse = new RefundResponse(txStateResponse);

            logger.Info(`# Error: ${txStateResponse.GetError()}`);
            logger.Info(`# Error Detail: ${txStateResponse.GetErrorDetail()}`);
            logger.Info(`# Response: ${refundResponse.GetResponseText()}`);
            logger.Info(`# RRN: ${refundResponse.GetRRN()}`);
            logger.Info(`# Scheme: ${refundResponse.SchemeName}`);
            logger.Info(`# Customer Receipt:`);
            logger.Info(
                !refundResponse.WasCustomerReceiptPrinted()
                    ? refundResponse.GetCustomerReceipt().trim()
                    : `# PRINTED FROM EFTPOS`
            );
        }
    },
    handleSuccessfulTransaction: (txStateResponse, logger) => {
        const refundResponse = new RefundResponse(txStateResponse);
        logger.Info(`# REFUND GIVEN - OH WELL!`);
        logger.Info(
            `# POS Reference: ${refundResponse.GetResponseValue("pos_ref_id")}`
        );
        logger.Info(`# Response: ${refundResponse.GetResponseText()}`);
        logger.Info(`# RRN: ${refundResponse.GetRRN()}`);
        logger.Info(`# Scheme: ${refundResponse.SchemeName}`);
        logger.Info(`# Customer Receipt:`);
        logger.Info(
            !refundResponse.WasCustomerReceiptPrinted()
                ? refundResponse.GetCustomerReceipt().trim()
                : `# PRINTED FROM EFTPOS`
        );
        logger.Info(`# REFUNDED AMOUNT: ${refundResponse.GetRefundAmount()}`);
    },
    handleUnknownState(logger) {
        logger.Info(
            `# WE'RE NOT QUITE SURE WHETHER THE REFUND WENT THROUGH OR NOT :/`
        );
        logger.Info(
            `# CHECK THE LAST TRANSACTION ON THE EFTPOS ITSELF FROM THE APPROPRIATE MENU ITEM.`
        );
        logger.Info(`# YOU CAN THE TAKE THE APPROPRIATE ACTION.`);
    },
};

const cashout = {
    initiateCashout: (spi, cashoutAmount, surchargeAmount, logger) => {
        if (!(cashoutAmount > 0)) {
            console.log("Cashout amount must be greater than 0");
            return false;
        }
        const posRefId = `cashout-${new Date().toISOString()}`;
        const res = spi.InitiateCashoutOnlyTx(
            posRefId,
            cashoutAmount,
            surchargeAmount
        );
        logger.Info(
            res.Initiated
                ? "# Cashout Initiated. Will be updated with Progress."
                : `# Could not initiate cashout: ${res.Message}. Please Retry.`
        );
    },
    handleSuccessfulTransaction: (txStateResponse, logger) => {
        const cashoutResponse = new CashoutOnlyResponse(txStateResponse);
        logger.Info(`# CASH-OUT SUCCESSFUL - HAND THEM THE CASH!`);
        logger.Info(
            `# POS Reference: ${cashoutResponse.GetResponseValue("pos_ref_id")}`
        );
        logger.Info(`# Response: ${cashoutResponse.GetResponseText()}`);
        logger.Info(`# RRN: ${cashoutResponse.GetRRN()}`);
        logger.Info(`# Scheme: ${cashoutResponse.SchemeName}`);
        logger.Info(`# Customer Receipt:`);
        if (cashoutResponse.WasCustomerReceiptPrinted()) {
            let receipt = cashoutResponse.GetCustomerReceipt().trim();
            //Do print receipt from POS integrated printer;
            logger.log(receipt);
        } else {
            logger.log(`# PRINTED FROM EFTPOS`);
        }
        logger.Info(`# CASHOUT: ${cashoutResponse.GetCashoutAmount()}`);
        logger.Info(
            `# BANKED NON-CASH AMOUNT: ${cashoutResponse.GetBankNonCashAmount()}`
        );
        logger.Info(`# BANKED CASH AMOUNT: ${cashoutResponse.GetBankCashAmount()}`);
        logger.Info(`# SURCHARGE: ${cashoutResponse.GetSurchargeAmount()}`);
    },
    handleFailedTransaction: (txStateResponse, logger) => {
        logger.Info(`# CASHOUT FAILED!`);
        if (txStateResponse != null) {
            const cashoutResponse = new CashoutOnlyResponse(txStateResponse);
            logger.Info(`# Error: ${txStateResponse.GetError()}`);
            logger.Info(`# Error Detail: ${txStateResponse.GetErrorDetail()}`);
            logger.Info(`# Response: ${cashoutResponse.GetResponseText()}`);
            logger.Info(`# RRN: ${cashoutResponse.GetRRN()}`);
            logger.Info(`# Scheme: ${cashoutResponse.SchemeName}`);
            logger.Info(`# Customer Receipt:`);
            logger.Info(cashoutResponse.GetCustomerReceipt());
        }
    },
    handleUnknownState() {
        logger.Info(
            `# WE'RE NOT QUITE SURE WHETHER THE CASHOUT WENT THROUGH OR NOT :/`
        );
        logger.Info(
            `# CHECK THE LAST TRANSACTION ON THE EFTPOS ITSELF FROM THE APPROPRIATE MENU ITEM.`
        );
        logger.Info(`# YOU CAN THE TAKE THE APPROPRIATE ACTION.`);
    },
};

const reversal = {
    initiateReversal: (spi, posRefId, logger) => {
        const res = spi.InitiateReversal(posRefId);
        logger.Info(
            res.Initiated
                ? "# Reversal Initiated. Will be updated with Progress."
                : `# Could not initiate reversal: ${res.Message}. Please Retry.`
        );
    },
    handleFailedTransaction: (txStateResponse, logger) => {
        logger.Info(`# WE DID NOT GET TRANSACTION-REVERTED :(`);
        if (txStateResponse != null) {
            const reversalResponse = new ReversalResponse(txStateResponse);
            logger.Info(`# Error Detail: ${reversalResponse.GetErrorDetail()}`);
            logger.Info(`# Error Reason:: ${reversalResponse.GetErrorReason()}`);
            logger.Info(`# Success: ${reversalResponse.Success}`);
            logger.Info(`# PosRefID: ${reversalResponse.PosRefId}`);
        }
    },
    handleSuccessfulTransaction: (txStateResponse, logger) => {
        const reversalResponse = new ReversalResponse(txStateResponse);
        logger.Info(`# WOOHOO - REVERSAL SUCCESSFULL`);
        logger.Info(`# Success: ${reversalResponse.Success}`);
        logger.Info(`# PosRefID: ${reversalResponse.PosRefId}`);
    },
    handleUnknownState: (logger) => {
        logger.Info(
            `# WE'RE NOT QUITE SURE WHETHER THE REVERSAL WENT THROUGH OR NOT :/`
        );
        logger.Info(`# YOU CAN THE TAKE THE APPROPRIATE ACTION.`);
    },
};

const transaction = {
    // Handle generic transaction completion flows
    acceptSignature: (spi) => {
        spi.AcceptSignature(true);
    },
    declineSignature: (spi) => {
        spi.AcceptSignature(false);
    },
    cancelTransaction: (spi) => {
        spi.CancelTransaction();
    },
    submitAuthCode: (spi, authCode, logger) => {
        const res = spi.SubmitAuthCode(authCode);
        logger.Info(
            res.ValidFormat
                ? `# Auth code submitted`
                : `# Invalid Code Format. ${res.Message}. Try Again.`
        );
    },
    acknowledgeCompletion: (spi, printStatusAndActions, logger) => {
        spi.AckFlowEndedAndBackToIdle();
        logger.Clear();
        logger.Info("Select from the options below");
        printStatusAndActions();
    },
    acknowledgeCancellation: (spi, printStatusAndActions, logger) => {
        spi.AckFlowEndedAndBackToIdle();

        logger.Clear();
        logger.Info("Order Cancelled");
        printStatusAndActions();
    },
    printReceipt: (spi, header, footer, apiKey) => {
        const payload = Pos.sanitizePrintText(header + footer);
        spi.PrintReceipt(apiKey, payload);
    },
    // Handle the generic start of a transaction
    handleTransaction: (txState, logger) => {
        logger.Info("### TX PROCESS UPDATE ###");
        logger.Info(`# ${txState.DisplayMessage}`);
        logger.Info(`# PosRefId: ${txState.PosRefId}`);
        logger.Info(`# Type: ${txState.Type}`);
        logger.Info(
            `# Request Amount: $${(txState.AmountCents / 100.0).toFixed(2)}`
        );
        logger.Info(`# Waiting For Signature: ${txState.AwaitingSignatureCheck}`);
        logger.Info(`# Attempting to Cancel : ${txState.AttemptingToCancel}`);
        logger.Info(`# Finished: ${txState.Finished}`);
        logger.Info(`# Success: ${txState.Success}`);
    },
    // Recovery operations
    initiateRecovery: (spi, posRefId, logger) => {
        logger.Clear();
        const res = spi.InitiateRecovery(posRefId, TransactionType.Purchase);
        logger.Info(
            res.Initiated
                ? "# Recovery Initiated. Will be updated with Progress."
                : `# Could not initiate recovery. ${res.Message}. Please Retry.`
        );
    },
    initiateGetTransaction: (spi, posRefId) => {
        const res = spi.InitiateGetTx(posRefId);
        return res;
    },
    handleGetTransaction: (txState, logger) => {
        console.log("handleGetTransaction");
        if (txState.Response !== null) {
            const purchaseResponse = new PurchaseResponse(txState.Response);
            logger.Info(`# Scheme: ${purchaseResponse.SchemeName}`);
            logger.Info(`# Response: ${purchaseResponse.GetResponseText()}`);
            logger.Info(`# RRN: ${purchaseResponse.GetRRN()}`);
            logger.Info(`# Error: ${txState.Response.GetError()}`);
            logger.Info(`# Customer Receipt:`);
            logger.Info(purchaseResponse.GetCustomerReceipt().trim());
        } else {
            // We did not even get a response, like in the case of a time-out.
            logger.Info("# Could Not Retrieve Transaction.");
        }
    },
    initiateGetLastTransaction: (spi, logger) => {
        const res = spi.InitiateGetLastTx();
        logger.Info(
            res.Initiated
                ? "# GLT Initiated. Will be updated with Progress."
                : `# Could not initiate GLT: ${res.Message}. Please Retry.`
        );
    },
    handleGetLastTransaction: (spi, txState, posRefId, logger) => {
        if (txState.Response != null) {
            const gltResponse = new GetLastTransactionResponse(txState.Response);
            if (posRefId && posRefId.length > 1) {
                // User specified that he intended to retrieve a specific tx by pos_ref_id
                // This is how you can use a handy function to match it.
                const success = spi.GltMatch(gltResponse, posRefId);
                if (success === SuccessState.Unknown) {
                    logger.Info(
                        "# Did not retrieve Expected Transaction. Here is what we got:"
                    );
                } else {
                    logger.Info("# Tx Matched Expected Purchase Request.");
                }
            }

            const purchaseResponse = new PurchaseResponse(txState.Response);
            logger.Info(`# Scheme: ${purchaseResponse.SchemeName}`);
            logger.Info(`# Response: ${purchaseResponse.GetResponseText()}`);
            logger.Info(`# RRN: ${purchaseResponse.GetRRN()}`);
            logger.Info(`# Error: ${txState.Response.GetError()}`);
            logger.Info(`# Customer Receipt:`);
            logger.Info(purchaseResponse.GetCustomerReceipt().trim());
        } else {
            // We did not even get a response, like in the case of a time-out.
            logger.Info("# Could Not Retrieve Last Transaction.");
        }
    },
};

const setting = {
    setAutoAddressResolutionState: (
        spi,
        autoResolveEftposAddress,
        testMode,
        useSecureWebSockets
    ) => {
        spi.SetTestMode(testMode);
        spi.SetSecureWebSockets(useSecureWebSockets);
        if (autoResolveEftposAddress != null) {
            spi.SetAutoAddressResolution(autoResolveEftposAddress);
        }
    },
    setIsMerchantReceiptPrinted: (
        spi,
        isMerchantReceiptPrinted,
        printStatusAndActions,
        logger
    ) => {
        spi.Config.PrintMerchantCopy = isMerchantReceiptPrinted;
        spi.AckFlowEndedAndBackToIdle();

        logger.Clear();
        printStatusAndActions();
    },
    setCustomReceiptStrings: (
        spi,
        options,
        customerReceiptHeader,
        customerReceiptFooter,
        merchantReceiptHeader,
        merchantReceiptFooter,
        printStatusAndActions,
        logger
    ) => {
        logger.Clear();
        spi._options = options;
        options.SetCustomerReceiptHeader(
            Pos.sanitizePrintText(customerReceiptHeader)
        );
        options.SetCustomerReceiptFooter(
            Pos.sanitizePrintText(customerReceiptFooter)
        );
        options.SetMerchantReceiptHeader(
            Pos.sanitizePrintText(merchantReceiptHeader)
        );
        options.SetMerchantReceiptFooter(
            Pos.sanitizePrintText(merchantReceiptFooter)
        );
        spi.AckFlowEndedAndBackToIdle();
        logger.Info("Receipt header / footer updated.");
        printStatusAndActions();
    },
    getTerminalStatus: (spi) => {
        spi.GetTerminalStatus();
    },
    onTxFlowStateChanged: (printStatusAndActions, logger) => {
        logger.Clear();
        printStatusAndActions();
        logger.Info("> ");
    },
    onPairingFlowStateChanged: (printStatusAndActions, logger) => {
        logger.Clear();
        printStatusAndActions();
        logger.Info("> ");
    },

    onSecretsChanged: (secrets) => {
        if (secrets != null) {
            console.log(`Have Secrets: Persist them Securely.`);
            window.localStorage.setItem("secrets", JSON.stringify(secrets));
        } else {
            console.log(
                `Have Lost the Secrets, i.e. Unpaired. Destroy the persisted secrets.`
            );
            window.localStorage.removeItem("secrets");
        }
    },

    saveSecrets: (encKey, hmacKey) => {
        if (encKey == null || encKey == "") {
            window.localStorage.removeItem("secrets");
        } else {
            var secrets = {};
            secrets.EncKey = encKey;
            secrets.HmacKey = hmacKey;
            window.localStorage.setItem("secrets", JSON.stringify(secrets));
        }
    },

    getSecrets: () => {
        const secretsString = localStorage.getItem("secrets") || "";
        try {
            const secrets = JSON.parse(secretsString);
            if (secrets) {
                return secrets;
            }
        } catch (error) {
            return "";
        }
    },

    onSpiStatusChanged: (printStatusAndActions, spiStatus, logger) => {
        logger.Clear();
        logger.Info(`# --> SPI Status Changed: ${spiStatus}`);
        printStatusAndActions();
    },
    handlePrintingResponse(spi, message, printStatusAndActions, logger) {
        logger.Clear();
        const printingResponse = new PrintingResponse(message);

        if (printingResponse.isSuccess()) {
            logger.Info("# --> Printing Response: Printing Receipt successful");
        } else {
            logger.Info(
                `# --> Printing Response:  Printing Receipt failed: reason = ${printingResponse.getErrorReason()}, detail = ${printingResponse.getErrorDetail()}`
            );
        }

        spi.AckFlowEndedAndBackToIdle();
        printStatusAndActions();
    },
    handleTerminalConfigurationResponse: (message, logger) => {
        const terminalConfigResponse = new TerminalConfigurationResponse(message);
        if (terminalConfigResponse.isSuccess()) {
            logger.Info("# Terminal Configuration Response #");
            logger.Info(
                `# Comms Method: ${terminalConfigResponse.GetCommsSelected()}`
            );
            logger.Info(`# MerchantId: ${terminalConfigResponse.GetMerchantId()}`);
            logger.Info(`# PA Version: ${terminalConfigResponse.GetPAVersion()}`);
            logger.Info(
                `# Payment Interface Version: ${terminalConfigResponse.GetPaymentInterfaceVersion()}`
            );
            logger.Info(
                `# Plugin Version: ${terminalConfigResponse.GetPluginVersion()}`
            );
            logger.Info(
                `# Serial Number: ${terminalConfigResponse.GetSerialNumber()}`
            );
            logger.Info(`# TerminalId: ${terminalConfigResponse.GetTerminalId()}`);
            logger.Info(
                `# Terminal Model: ${terminalConfigResponse.GetTerminalModel()}`
            );
        }
    },
    handleTerminalStatusResponse: (
        spi,
        message,
        printStatusAndActions,
        logger
    ) => {
        logger.Clear();
        const terminalStatusResponse = new TerminalStatusResponse(message);
        logger.Info("# Terminal Status Response #");
        logger.Info(`# Status: ${terminalStatusResponse.GetStatus()}`);
        logger.Info(
            `# Battery Level: ${terminalStatusResponse.GetBatteryLevel()}%`
        );
        logger.Info(`# Charging: ${terminalStatusResponse.IsCharging()}`);
        spi.AckFlowEndedAndBackToIdle();
        printStatusAndActions();
    },
    handleTransactionUpdateMessage: (message, logger) => {
        const txnUpdateMessage = new TransactionUpdate(message);
        logger.Info(`${txnUpdateMessage.GetDisplayMessageText()}`);
    },
    handleBatteryLevelChanged: (spi, message, printStatusAndActions, logger) => {
        logger.clear();
        const terminalBattery = new TerminalBattery(message);
        logger.Info("# Battery Level Changed #");
        logger.Info(`# Battery Level: ${terminalBattery.BatteryLevel}%`);
        spi.AckFlowEndedAndBackToIdle();
        printStatusAndActions();
    },
    isUnknownStatus: (spi) => {
        if (spi.CurrentFlow === SpiFlow.Transaction) {
            if (
                spi.CurrentTxFlowState.Finished &&
                spi.CurrentTxFlowState.Success === SuccessState.Unknown
            ) {
                return true;
            }
        }
        return false;
    },
};

const settlement = {
    initiateSettlement: (spi, logger) => {
        const res = spi.InitiateSettleTx(RequestIdHelper.Id("settle"));
        return res;
    },
    handleFailedTransaction: (txStateResponse, logger) => {
        logger.Info(`# SETTLEMENT FAILED!`);
        if (txStateResponse != null) {
            const settlementResponse = new Settlement(txStateResponse);
            logger.Info(`# Response: ${settlementResponse.GetResponseText()}`);
            logger.Info(`# Error: ${txStateResponse.GetError()}`);
            logger.Info(`# Merchant Receipt:`);
            logger.Info(settlementResponse.GetReceipt());
        }
    },
    handleSuccessfulTransaction: (txStateResponse, logger) => {
        logger.Info(`# SETTLEMENT SUCCESSFUL!`);
        if (txStateResponse != null) {
            const settlementResponse = new Settlement(txStateResponse);
            logger.Info(`# Response: ${settlementResponse.GetResponseText()}`);
            logger.Info(`# Merchant Receipt:`);
            //Print Receipt
            logger.Info(settlementResponse.GetReceipt().trim());
            logger.Info(`# Period Start: ${settlementResponse.GetPeriodStartTime()}`);
            logger.Info(`# Period End: ${settlementResponse.GetPeriodEndTime()}`);
            logger.Info(
                `# Settlement Time: ${settlementResponse.GetTriggeredTime()}`
            );
            logger.Info(
                `# Transaction Range: ${settlementResponse.GetTransactionRange()}`
            );
            logger.Info(`# Terminal Id: ${settlementResponse.GetTerminalId()}`);
            logger.Info(`# Total TX Count: ${settlementResponse.GetTotalCount()}`);
            logger.Info(
                `# Total TX Value: $${(
                    settlementResponse.GetTotalValue() / 100.0
                ).toFixed(2)}`
            );
            logger.Info(
                `# By Acquirer TX Count: ${settlementResponse.GetSettleByAcquirerCount()}`
            );
            logger.Info(
                `# By Acquirer TX Value: $${(
                    settlementResponse.GetSettleByAcquirerValue() / 100.0
                ).toFixed(2)}`
            );
            logger.Info(`# SCHEME SETTLEMENTS:`);
            const schemes = settlementResponse.GetSchemeSettlementEntries();
            schemes.forEach((s) => {
                logger.Info(`# ${JSON.stringify(schemes[s])}`);
            });
        }
    },
    handleUnknownState: (logger) => {
        logger.Info(`# SETTLEMENT ENQUIRY RESULT UNKNOWN!`);
    },
};

const settlementEnquiry = {
    initiateSettlementEnquiry: (spi, logger) => {
        const res = spi.InitiateSettlementEnquiry(RequestIdHelper.Id("stlenq"));
        logger.Info(
            res.Initiated
                ? "# Settle enquiry Initiated. Will be updated with Progress."
                : `# Could not initiate settle enquiry: ${res.Message}. Please Retry.`
        );
    },
    handleFailedTransaction: (txStateResponse, logger) => {
        logger.Info(`# SETTLEMENT ENQUIRY FAILED!`);
        if (txStateResponse != null) {
            const settleResponse = new Settlement(txStateResponse);
            logger.Info(`# Response: ${settleResponse.GetResponseText()}`);
            logger.Info(`# Error: ${txStateResponse.GetError()}`);
            logger.Info(`# Merchant Receipt:`);
            logger.Info(settleResponse.GetReceipt());
        }
    },
    handleSuccessfulTransaction: (txStateResponse, logger) => {
        logger.Info(`# SETTLEMENT ENQUIRY SUCCESSFUL!`);
        if (txStateResponse != null) {
            const settleResponse = new Settlement(txStateResponse);
            logger.Info(`# Response: ${settleResponse.GetResponseText()}`);
            logger.Info(`# Merchant Receipt:`);
            //Print receipt
            logger.Info(settleResponse.GetReceipt().trim());
            logger.Info(`# Period Start: ${settleResponse.GetPeriodStartTime()}`);
            logger.Info(`# Period End: ${settleResponse.GetPeriodEndTime()}`);
            logger.Info(`# Settlement Time: ${settleResponse.GetTriggeredTime()}`);
            logger.Info(
                `# Transaction Range: ${settleResponse.GetTransactionRange()}`
            );
            logger.Info(`# Terminal Id: ${settleResponse.GetTerminalId()}`);
            logger.Info(`# Total TX Count: ${settleResponse.GetTotalCount()}`);
            logger.Info(
                `# Total TX Value: $${(settleResponse.GetTotalValue() / 100.0).toFixed(
                    2
                )}`
            );
            logger.Info(
                `# By Acquirer TX Count: ${settleResponse.GetSettleByAcquirerCount()}`
            );
            logger.Info(
                `# By Acquirer TX Value: $${(
                    settleResponse.GetSettleByAcquirerValue() / 100.0
                ).toFixed(2)}`
            );
            logger.Info(`# SCHEME SETTLEMENTS:`);
            const schemes = settleResponse.GetSchemeSettlementEntries();
            schemes.forEach((s) => {
                logger.Info(`# ${JSON.stringify(schemes[s])}`);
            });
        }
    },
    handleUnknownState: (logger) => {
        logger.Info(`# SETTLEMENT ENQUIRY RESULT UNKNOWN!`);
    },
};

const utility = {
    // Cleans RTF strings so that the terminal will accept them
    sanitizePrintText: (content) => {
        let sanitizedText = content.replace("\\emphasis", "emphasis");
        sanitizedText = sanitizedText.replace("\\clear", "clear");
        return sanitizedText.replace("\r\n", "\n");
    },
    processCompletedEvent: (eventType, transactionState, logger) => {
        const transactionStateMap = {
            [SuccessState.Failed]: eventType.handleFailedTransaction,
            [SuccessState.Success]: eventType.handleSuccessfulTransaction,
            [SuccessState.Unknown]: eventType.handleUnknownState,
        };
        const transactionStateHandler =
            transactionStateMap[transactionState.Success];
        if (!transactionStateHandler) {
            throw new Error("Unknown transaction state");
        }
        transactionStateHandler(transactionState.Response, logger);
    },
    isTransactionInquiry: (transactionType) =>
        [
            TransactionType.GetTransaction,
            TransactionType.GetLastTransaction,
        ].includes(transactionType),
};

const helpers = {
    pairing,
    purchase,
    refund,
    cashout,
    reversal,
    transaction,
    setting,
    settlement,
    settlementEnquiry,
};
