/*
 * Copyright (C) 2018 Google Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */
package com.google.rbm.samples;

// [START of the chat bot for the Banking Agent]

// [START import_libraries]

import com.google.api.services.rcsbusinessmessaging.v1.RCSBusinessMessaging;
import com.google.api.services.rcsbusinessmessaging.v1.model.*;
import com.google.appengine.api.datastore.*;
import com.google.protobuf.Value;
import com.google.rbm.samples.dialogflow.DialogFlowHelper;
import com.google.rbm.samples.dialogflow.DialogFlowResponse;
import com.google.rbm.samples.lib.RbmApiHelper;
import com.google.rbm.samples.lib.SuggestionHelper;
import com.google.rbm.samples.lib.cards.CardOrientation;
import com.google.rbm.samples.lib.cards.MediaHeight;

import java.io.IOException;
import java.text.NumberFormat;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
// [END import_libraries]

/**
 * Chat bot for the Banking Agent demo.
 *
 * All conversation is handled by this class.
 */
public class BankingAgentBot {
    // logger for info and error messages
    private static final Logger logger = Logger.getLogger(BankingAgentBot.class.getName());

    private static final String EXCEPTION_WAS_THROWN = "an exception was thrown";

    // reference to the RBM api builder
    private RCSBusinessMessaging.Builder builder;

    // the phone number, in E.164 format, to start a conversation with
    private String msisdn;

    // wrapper class for the RBM API, makes calls simpler
    private RbmApiHelper rbmApiHelper;

    // wrapper class for communicating with the DialogFlow API
    private DialogFlowHelper dialogFlowHelper;

    // used as an example to show the closest branch location when someone asks to open an account
    private static final String ACME_BANK_BRANCH_LOCATION
            = "https://storage.googleapis.com/acme-banking-agent.appspot.com/branch-map.png";

    // used in richcard when someone decides to speak directly with an agent
    private static final String ACME_BANK_AGENT_IMAGE
            = "https://storage.googleapis.com/acme-banking-agent.appspot.com/sean.jpg";

    // constant for suggestion to open an account
    private static final SuggestionHelper CHECKING_ACCOUNT_OPTION
            = new SuggestionHelper("Checking", "checking");

    // constant for suggestion to open an account
    private static final SuggestionHelper SAVINGS_ACCOUNT_OPTION
            = new SuggestionHelper("Savings", "savings");

    // constant for suggestion to open an account
    private static final SuggestionHelper OPEN_ACCOUNT_OPTION
            = new SuggestionHelper("Open Account", "open_account");

    // constant for suggestion to check an account's balance
    private static final SuggestionHelper CHECK_BALANCE_OPTION
            = new SuggestionHelper("Check Balance", "check_balance");

    // constant for suggestion to transfer money between accounts
    private static final SuggestionHelper TRANSFER_MONEY_OPTION
            = new SuggestionHelper("Transfer Money", "transfer");

    // constant for suggestion to check earnings on an account
    private static final SuggestionHelper EARNINGS_OPTION
            = new SuggestionHelper("Check Earnings", "earnings");

    // constant for suggestion to check earnings on an account
    private static final SuggestionHelper CALL_AGENT_OPTION
            = new SuggestionHelper("Call Us", "call");

    // constant for suggestion to check earnings on an account
    private static final SuggestionHelper BRANCH_LOOKUP_OPTION
            = new SuggestionHelper("Look Up Branch", "look_up");

    // constant for suggestion to check earnings on an account
    private static final SuggestionHelper DIRECTIONS_OPTION
            = new SuggestionHelper("Get Directions", "directions");

    // constant for suggestion to transfer $100
    private static final SuggestionHelper HUNDRED_DOLLAR_OPTION
            = new SuggestionHelper("$100", "$100");

    // constant for suggestion to transfer $500
    private static final SuggestionHelper FIVE_HUNDRED_DOLLAR_OPTION
            = new SuggestionHelper("$500", "$500");

    // constant for suggestion to transfer $1,000
    private static final SuggestionHelper ONE_THOUSAND_DOLLAR_OPTION
            = new SuggestionHelper("$1,000", "$1000");

    // constant for suggestion to transfer $5,000
    private static final SuggestionHelper FIVE_THOUSAND_DOLLAR_OPTION
            = new SuggestionHelper("$5,000", "$5000");

    // constant for suggestion to say yes to confirm an account transfer
    private static final SuggestionHelper YES_TRANSFER_OPTION
            = new SuggestionHelper("Yes", "yes");

    // constant for suggestion to say no when confirming an account transfer
    private static final SuggestionHelper NO_TRANSFER_OPTION
            = new SuggestionHelper("No", "no");

    // constants for postback responses that should be ignored when determining a response
    private static final String IGNORE_POST_BACKS[] = {DIRECTIONS_OPTION.getPostbackData(),
            BRANCH_LOOKUP_OPTION.getPostbackData(), CALL_AGENT_OPTION.getPostbackData()};

    /**
     * Constructor for the Banking Agent bot.
     * @param msisdn The phone number in E.164 format.
     */
    public BankingAgentBot(String msisdn) {
        this.msisdn = msisdn;

        this.rbmApiHelper = new RbmApiHelper();
        this.dialogFlowHelper = new DialogFlowHelper();
    }

    /**
     * Sends the initial greeting to the user.
     */
    public void sendGreeting() throws IOException {
        List<Suggestion> suggestedActions = getDefaultSuggestionList();

        // create a standalone card for the welcome message
        StandaloneCard standaloneCard = rbmApiHelper.createStandaloneCard(
                "Hi there. Welcome to ACME Bank.",
                "Here's what I can help you with:",
                null,
                MediaHeight.TALL,
                CardOrientation.VERTICAL,
                suggestedActions
        );

        rbmApiHelper.sendStandaloneCard(standaloneCard, msisdn);
    }

    /**
     * Checks to see if we are doing a live chat hand off. If the client has indicated they
     * want to speak to an agent, i.e. "message_agent" postback, is received or we have an
     * on-going chat, then we know we need to do a direct message to the client.
     * @param responseText
     * @return True if we should be doing direct messaging with the client.
     */
    public boolean isDirectMessage(String responseText) {
        return chatExists() || responseText.contains("message_agent");
    }

    /**
     * Removes all chat history.
     */
    public void resetChat() {
        logger.info("resetChats");

        DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();

        // retrieve all messages associated with the msisdn
        Query q = new Query("Message")
                .setFilter(
                        new Query.FilterPredicate("msisdn",
                                Query.FilterOperator.EQUAL,
                                msisdn)
                );

        PreparedQuery pq = datastore.prepare(q);

        List<Entity> messages = pq.asList(FetchOptions.Builder.withLimit(50));

        logger.info("messages: " + messages.size());

        // delete all messages from the datastore
        for(Entity result: messages) {
            logger.info("removing message");
            datastore.delete(result.getKey());
        }
    }

    /**
     * Gets all messages from the conversation with the matching msisdn.
     * @return List of messages as Entity objects.
     */
    public List<Entity> getMessages() {
        DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();

        logger.info("msisdn: " + msisdn);

        // retrieve all messages matching the msisdn
        final Query q = new Query("Message")
                .setFilter(
                        new Query.FilterPredicate("msisdn",
                                Query.FilterOperator.EQUAL,
                                msisdn)
                );

        PreparedQuery pq = datastore.prepare(q);

        List<Entity> messages = pq.asList(FetchOptions.Builder.withLimit(50));

        // sort results by date
        Collections.sort(messages, new Comparator<Entity>() {
            @Override
            public int compare(Entity o1, Entity o2) {
                Long createdDate1 = Long.parseLong(o1.getProperty("created_date").toString());
                Long createdDate2 = Long.parseLong(o2.getProperty("created_date").toString());

                return createdDate1.compareTo(createdDate2);
            }
        });

        return messages;
    }

    /**
     * Adds a new message to the conversation datastore.
     * @param responseText The text for the message body.
     * @param sender The person that sent the message.
     */
    public void pushMessage(String responseText, String sender) {
        try {
            logger.info("appending to file " + responseText);

            DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();

            // create a new message for the datastore
            Entity message = new Entity("Message");
            message.setProperty("msisdn", msisdn);
            message.setProperty("body", responseText);
            message.setProperty("sender", sender);
            message.setProperty("created_date", new Date().getTime());

            datastore.put(message);
        } catch (Exception e) {
            logger.log(Level.SEVERE, EXCEPTION_WAS_THROWN, e);
        }
    }

    /**
     * Sends the message via the RBM api to the msisdn and also saves it into
     * the chat datastore for this conversation.
     * @param message The message text to send.
     * @throws IOException
     */
    public void sendCustomerSupportMessage(String message) throws IOException {
        // let the client know we are creating a response
        rbmApiHelper.sendIsTypingMessage(msisdn);

        // add message to datastore
        pushMessage(message, "me");

        // send message via RBM to the client
        rbmApiHelper.sendTextMessage(message, msisdn);
    }

    /**
     * Response message handler for client responses.
     * @param userResponse The text of the user response.
     */
    public void handleResponse(String userResponse) throws IOException {
        logger.info("Handling the user response: "  + userResponse);

        // first convert any postback responses into sentences for DialogFlow
        userResponse = transformReplyToText(userResponse);

        String cleanResponseText = userResponse.toLowerCase();

        logger.info("Transformed user response: "  + userResponse);

        if(!shouldIgnoreResponse(cleanResponseText)) {
            logger.info("sending typing response");

            // let the client know we are creating a response
            rbmApiHelper.sendIsTypingMessage(msisdn);

            // display the menu if the user types menu
            if(cleanResponseText.contains("menu")) {
                sendReEngageMessage("Here's what I can help you with.");
            }
            else {
                logger.info("Passing to Dialogflow");

                // use DialogFlow to determine the user's intent
                DialogFlowResponse response = dialogFlowHelper.detectIntentTexts(userResponse, msisdn);

                if(response.getDisplayName().contains("account.open")) {
                    logger.info("Open account");

                    handleOpenAccount(response);
                }
                else if(response.getDisplayName().contains("account.balance.check")) {
                    handleBalanceCheck(response);
                }
                else if(response.getDisplayName().contains("account.spending.check")) {
                    handleSpending(response);
                }
                else if(response.getDisplayName().contains("account.earning.check")) {
                    handleEarning(response);
                }
                else if(response.getDisplayName().contains("transfer.money")) {
                    handleMoneyTransfer(response);
                }
                else if(response.getDisplayName().equals("Default Fallback Intent")) {
                    if(userResponse.equals("no")) {
                        rbmApiHelper.sendTextMessage("Fantastic! Thank you for banking with " +
                                "ACME Bank. Have a great day!", msisdn);
                    }
                    else {
                        sendReEngageMessage(response.getResponseText());
                    }
                }
                else if(response.getDisplayName().equals("Default Welcome Intent")) {
                    sendReEngageMessage("Hi there. Here's what I can help you with.");
                }
                else {
                    // not sure what the intent is, send the response directly
                    rbmApiHelper.sendTextMessage(response.getResponseText(), msisdn);
                }
            }
        }
    }

    /**
     * Checks to see if an on-going live chat is happening by seeing if there's any messages
     * in the chat datastore for the msisdn.
     * @return True if there is a chat started, otherwise False.
     */
    private boolean chatExists() {
        DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();

        // query to get messages matching the msisdn
        Query q = new Query("Message")
                .setFilter(
                        new Query.FilterPredicate("msisdn",
                                Query.FilterOperator.EQUAL,
                                msisdn)
                );

        PreparedQuery pq = datastore.prepare(q);

        List<Entity> messages = pq.asList(FetchOptions.Builder.withLimit(1));

        return messages.size() > 0;
    }

    /**
     * Transforms a postback response from a client into a sentence that can be
     * passed to DialogFlow for detecting intent.
     * @param response The postback response from a client.
     * @return The sentence representation of the postback.
     */
    private String transformReplyToText(String response) {
        if(response.equals(OPEN_ACCOUNT_OPTION.getPostbackData())) {
            return "I want to open an account";
        }
        else if(response.equals(CHECK_BALANCE_OPTION.getPostbackData())) {
            return "I want to check my balance";
        }
        else if(response.equals(TRANSFER_MONEY_OPTION.getPostbackData())) {
            return "I want to transfer money";
        }
        else if(response.equals(EARNINGS_OPTION.getPostbackData())) {
            return "I want to check my earnings";
        }

        return response;
    }

    /**
     * Checks the input response against known client actions that we should ignore, like
     * opening a map or making a phone call.
     * @param response The client's text response.
     * @return True if we should ignore the response, false if we should handle it.
     */
    private boolean shouldIgnoreResponse(String response) {
        for(String ignoreString: IGNORE_POST_BACKS) {
            if(response.equals(ignoreString)) return true;
        }

        return false;
    }

    /**
     * Mimics a hand off to a real person for handling a request outside the scope of the bot.
     * @param responseText The response from the client.
     * @throws IOException
     */
    public void handleAgentDirectMessage(String responseText) throws IOException {
        logger.info("handleAgentDirectMessage: "  + responseText);

        if(responseText.contains("message_agent")) {
            // create a standalone card for the rail ticket
            StandaloneCard standaloneCard = rbmApiHelper.createStandaloneCard(
                    "You are now speaking with Sean.",
                    "Hi there, my name is Sean. How can I help you?",
                    ACME_BANK_AGENT_IMAGE,
                    MediaHeight.TALL,
                    CardOrientation.VERTICAL,
                    null
            );

            pushMessage("Hi there, my name is Sean. How can I help you?", "me");

            try {
                rbmApiHelper.sendStandaloneCard(standaloneCard, msisdn);
            } catch(IOException e) {
                logger.log(Level.SEVERE, EXCEPTION_WAS_THROWN, e);
            }
        }
        else { // write message from user to file
            pushMessage(responseText, "them");
        }
    }

    /**
     * Sends the client the response from DialogFlow and the default suggestions for
     * situations where we need to re-engage the client.
     * @param responseText The text to send the client.
     */
    private void sendReEngageMessage(String responseText) {
        List<Suggestion> suggestedActions = getDefaultSuggestionList();

        Suggestion suggestion = new Suggestion();

        // create a reply option to message with a person
        SuggestedReply reply = new SuggestedReply();
        reply.setText("Customer Support");
        reply.setPostbackData("message_agent");

        suggestion.setReply(reply);

        suggestedActions.add(suggestion);

        // create a standalone card for the rail ticket
        StandaloneCard standaloneCard = rbmApiHelper.createStandaloneCard(
                responseText,
                null,
                null,
                MediaHeight.TALL,
                CardOrientation.VERTICAL,
                suggestedActions
        );

        try {
            rbmApiHelper.sendStandaloneCard(standaloneCard, msisdn);
        } catch(IOException e) {
            logger.log(Level.SEVERE, EXCEPTION_WAS_THROWN, e);
        }
    }

    /**
     * Annotates the Dialogflow response when someone asks about their spending.
     * This function creates some fake data to report to the user.
     * @param response The response sent back from the Dialogflow API
     * @throws IOException
     */
    private void handleSpending(DialogFlowResponse response) throws IOException {
        // get the suggested text from DialogFlow
        String responseText = response.getResponseText() + "\n";

        // initialize how many fake withdrawals we are going to create
        int fakeWithdrawalCount = (int)(1 + Math.random() * 5);

        Locale locale = new Locale("en", "US");
        NumberFormat currencyFormatter = NumberFormat.getCurrencyInstance(locale);

        for(int i = 0; i < fakeWithdrawalCount; i++) {
            double dollarAmount = Math.random() * 1000;

            responseText += "\n" + currencyFormatter.format(dollarAmount);
        }

        // send the client the response
        rbmApiHelper.sendTextMessage(
                responseText,
                this.msisdn
        );
    }

    /**
     * Annotates the Dialogflow response when someone asks about their earnings.
     * This function creates some fake data to report to the user.
     * @param response The response sent back from the Dialogflow API
     * @throws IOException
     */
    private void handleEarning(DialogFlowResponse response) throws IOException {
        // get the suggested text from DialogFlow
        String responseText = response.getResponseText() + "\n";

        // initialize how many fake deposits we are going to create
        int fakeDepositCount = (int)(1 + Math.random() * 5);

        Locale locale = new Locale("en", "US");
        NumberFormat currencyFormatter = NumberFormat.getCurrencyInstance(locale);

        for(int i = 0; i < fakeDepositCount; i++) {
            double dollarAmount = Math.random() * 1000;

            responseText += "\n" + currencyFormatter.format(dollarAmount);
        }

        // send the client the response
        rbmApiHelper.sendTextMessage(
                responseText,
                this.msisdn
        );
    }

    /**
     * Handles all the situations when a client wants to transfer money from one account to another.
     * @param response The response object from DialogFlow
     */
    private void handleMoneyTransfer(DialogFlowResponse response) {
        // get the suggested text from DialogFlow
        String responseText = response.getResponseText();

        // this will keep track of the account the client wants to transfer to
        String transferToAccount = "";

        // version of the response used for business logic
        String cleanResponseText = responseText.toLowerCase();

        if(responseText.contains("from which account?")) {
            transferToAccount = getTransferToAccountInformation(response);

            if(transferToAccount.length() == 0) {
                responseText = "Sure. Transfer to which account?";
            }
            else {
                logger.info("Transfer to an account, figure it out: " + response.getResponseText());

                // we know what account to transfer to, and there are only two accounts, so we
                // can predict what the transfer from account should be
                response = figureOutTransferFromAccount(transferToAccount);

                // update the response information
                responseText = response.getResponseText();
                cleanResponseText = responseText.toLowerCase();
            }
        }

        // figure out the context of the conversation and pass off the response handling
        // to the appropriate function
        if(cleanResponseText.contains("how much do you want to transfer?")) {
            sendTransferAmountSuggestion(responseText);
        }
        else if(cleanResponseText.contains("from which account?")
                || cleanResponseText.contains("to which account?")) {
            sendTransferAccountSuggestion(responseText, transferToAccount);
        }
        else if(cleanResponseText.contains("is that right?")) {
            sendTransferConfirmation(responseText);
        }
        else if(cleanResponseText.contains("your confirmation number is:")) {
            sendConfirmationNumber(responseText);

            sendReEngageMessage("Is there anything else I can help you with?");
        }
        else {
            sendReEngageMessage(responseText);
        }
    }

    /**
     * Attempts to find what account has already been selected as the destination for a transfer.
     * @return The account name, checking or savings.
     */
    private String getTransferToAccountInformation(DialogFlowResponse response) {
        String transferToAccount = "";

        // Because Dialogflow's bank agent does not distinguish between sending from
        // and sending to an account, we have to do some funky processing here to switch the
        // responding text based on what parameters are filled out
        for(Map.Entry<String, Value> entry: response.getFields().entrySet()) {
            if(entry.getKey().contains("account-to")) {
                if(!entry.getValue().getStringValue().equals("")) {
                    transferToAccount = entry.getValue().getStringValue();
                }
            }
        }

        return transferToAccount;
    }

    /**
     * We know the transfer to account, so we can construct the transfer from account and pass
     * that sentences to DialogFlow to update the context. This helps skip asking the client
     * for information that we should already know.
     * @param transferToAccount The account the client wants to transfer to.
     * @return The new DialogFlow context.
     */
    private DialogFlowResponse figureOutTransferFromAccount(String transferToAccount) {
        String predictedResponse = "";
        if(transferToAccount.equals("checking account")) {
            predictedResponse = "savings";
        }
        else {
            predictedResponse = "checking";
        }

        logger.info("predictedResponse: " + predictedResponse);

        // use DialogFlow to determine the user's intent
        return dialogFlowHelper.detectIntentTexts(predictedResponse, msisdn);
    }

    /**
     * Transfer is complete, send the confirmation number to the client.
     * @param responseText The suggested response from DialogFlow.
     */
    private void sendConfirmationNumber(String responseText) {
        // generate a random number for the confirmation number for demo purposes
        int confirmationNumber = (int)(Math.random() * 10000);

        // append the confirmation number to the response
        responseText += " " + confirmationNumber;

        try {
            // send the client the response
            rbmApiHelper.sendTextMessage(
                    responseText,
                    this.msisdn
            );
        } catch(IOException e) {
            logger.log(Level.SEVERE, EXCEPTION_WAS_THROWN, e);
        }
    }

    /**
     * Sends the client a message to confirm an account transfer.
     * @param responseText The text prompt to send the client.
     */
    private void sendTransferConfirmation(String responseText) {
        // we need to ask the client for confirmation for the transfer
        List<Suggestion> suggestedActions = new ArrayList<Suggestion>();

        suggestedActions.add(YES_TRANSFER_OPTION.getSuggestedReply());
        suggestedActions.add(NO_TRANSFER_OPTION.getSuggestedReply());

        try {
            // convert the dollar amount to a nicely formatted dollar amount
            responseText = convertAmountToPrettyString(responseText);

            // send the client the prompt and suggested responses
            rbmApiHelper.sendTextMessage(
                    responseText,
                    this.msisdn,
                    suggestedActions
            );
        } catch(IOException e) {
            logger.log(Level.SEVERE, EXCEPTION_WAS_THROWN, e);
        }
    }

    private String convertAmountToPrettyString(String input) {
        Locale locale = new Locale("en", "US");
        NumberFormat currencyFormatter = NumberFormat.getCurrencyInstance(locale);

        // create a regex to find the number in the input
        Pattern p = Pattern.compile("\\d+");
        Matcher m = p.matcher(input);

        // found the number, convert it into a pretty print currency
        if(m.find()) {
            String formattedAmount = currencyFormatter.format(Double.parseDouble(m.group()));

            input = input.replace(m.group(), formattedAmount);
        }

        return input;
    }

    /**
     * Sends the client a question to ask which account they want to transfer money from/to.
     * @param responseText The text to send the client.
     */
    private void sendTransferAccountSuggestion(String responseText, String transferToAccout) {
        // we need to ask the client for the account they want to check the balance on
        List<Suggestion> suggestedActions = new ArrayList<Suggestion>();

        // stripping the "Sure." from the response to make the conversation flow better
        if(responseText.equals("Sure. Transfer from which account?")) {
            responseText = "Transfer from which account?";
        }

        // check to see if the client already selected a to account
        if(transferToAccout.contains("checking")) {
            suggestedActions.add(SAVINGS_ACCOUNT_OPTION.getSuggestedReply());
        }
        else if(transferToAccout.contains("savings")) {
            suggestedActions.add(CHECKING_ACCOUNT_OPTION.getSuggestedReply());
        }
        else {
            suggestedActions.add(SAVINGS_ACCOUNT_OPTION.getSuggestedReply());
            suggestedActions.add(CHECKING_ACCOUNT_OPTION.getSuggestedReply());
        }

        try {
            // send the client the prompt and suggested responses
            rbmApiHelper.sendTextMessage(
                    responseText,
                    this.msisdn,
                    suggestedActions
            );
        } catch(IOException e) {
            logger.log(Level.SEVERE, EXCEPTION_WAS_THROWN, e);
        }
    }

    /**
     * Sends the client a message to check how much money they want to transfer along with
     * some suggested amounts.
     * @param responseText The text to send the client.
     */
    private void sendTransferAmountSuggestion(String responseText) {
        // provide the client with some domination suggestions
        List<Suggestion> suggestedActions = new ArrayList<Suggestion>();

        suggestedActions.add(HUNDRED_DOLLAR_OPTION.getSuggestedReply());
        suggestedActions.add(FIVE_HUNDRED_DOLLAR_OPTION.getSuggestedReply());
        suggestedActions.add(ONE_THOUSAND_DOLLAR_OPTION.getSuggestedReply());
        suggestedActions.add(FIVE_THOUSAND_DOLLAR_OPTION.getSuggestedReply());

        try {
            // send the client the prompt and suggested responses
            rbmApiHelper.sendTextMessage(
                    responseText,
                    this.msisdn,
                    suggestedActions
            );
        } catch(IOException e) {
            logger.log(Level.SEVERE, EXCEPTION_WAS_THROWN, e);
        }
    }

    /**
     * Sends the client their balance or a prompt to check which account they want to check.
     * @param response The DialogFlow response object.
     */
    private void handleBalanceCheck(DialogFlowResponse response) {
        // check to see if the balance check is complete or whether we are reporting a balance here
        if(response.isComplete()
                || response.getResponseText().contains("balance for this account:")) {
            double dollarAmount = Math.random() * 10000;

            Locale locale = new Locale("en", "US");
            NumberFormat currencyFormatter = NumberFormat.getCurrencyInstance(locale);

            String responseText = response.getResponseText()
                    + " " + currencyFormatter.format(dollarAmount);

            try {
                rbmApiHelper.sendTextMessage(responseText, msisdn);

                sendReEngageMessage("Is there anything else I can help you with?");
            } catch(IOException e) {
                logger.log(Level.SEVERE, EXCEPTION_WAS_THROWN, e);
            }
        }
        else {
            // we need to ask the client for the account they want to check the balance on
            List<Suggestion> suggestedActions = new ArrayList<Suggestion>();
            suggestedActions.add(CHECKING_ACCOUNT_OPTION.getSuggestedReply());
            suggestedActions.add(SAVINGS_ACCOUNT_OPTION.getSuggestedReply());

            try {
                // send the client the prompt and suggested responses
                rbmApiHelper.sendTextMessage(
                        response.getResponseText(),
                        this.msisdn,
                        suggestedActions
                );
            } catch(IOException e) {
                logger.log(Level.SEVERE, EXCEPTION_WAS_THROWN, e);
            }
        }
    }

    /**
     * Handles the situation where a client wants to open a new account.
     * @param response The response object from DialogFlow
     */
    private void handleOpenAccount(DialogFlowResponse response) {
        List<Suggestion> suggestedActions = new ArrayList<Suggestion>();

        // create a dial action so the client can call customer service
        DialAction dialAction = new DialAction();
        dialAction.setPhoneNumber("+12223334444");

        // setup the suggested action text and postback values
        SuggestedAction suggestedAction = new SuggestedAction();
        suggestedAction.setDialAction(dialAction);
        suggestedAction.setText(CALL_AGENT_OPTION.getText());
        suggestedAction.setPostbackData(CALL_AGENT_OPTION.getPostbackData());

        Suggestion suggestion = new Suggestion();

        // attach the suggested action
        suggestion.setAction(suggestedAction);

        // add to the list of suggestions
        suggestedActions.add(suggestion);

        // create an open url action for looking up a branch
        OpenUrlAction openUrlAction = new OpenUrlAction();
        openUrlAction.setUrl("https://www.google.com/maps");

        // attach the open url action to a suggested action
        suggestedAction = new SuggestedAction();
        suggestedAction.setOpenUrlAction(openUrlAction);
        suggestedAction.setText(BRANCH_LOOKUP_OPTION.getText());
        suggestedAction.setPostbackData(BRANCH_LOOKUP_OPTION.getPostbackData());

        // attach the action to a suggestion object
        suggestion = new Suggestion();
        suggestion.setAction(suggestedAction);

        suggestion = new Suggestion();

        // attach the suggested action
        suggestion.setAction(suggestedAction);

        // add to the list of suggestions
        suggestedActions.add(suggestion);

        try {
            // create a standalone card for the open account response
            StandaloneCard standaloneCard = rbmApiHelper.createStandaloneCard(
                    "Opening an account:",
                    response.getResponseText(),
                    ACME_BANK_BRANCH_LOCATION,
                    MediaHeight.TALL,
                    CardOrientation.VERTICAL,
                    suggestedActions
            );

            // sends a text response with some suggested actions to take and
            // a map showing a local branch
            rbmApiHelper.sendStandaloneCard(standaloneCard, msisdn);
        } catch(IOException e) {
            logger.log(Level.SEVERE, EXCEPTION_WAS_THROWN, e);
        }
    }

    /**
     * Creates the default set of suggested replies for a client.
     * @return A list of suggested replies.
     */
    private List<Suggestion> getDefaultSuggestionList() {
        List<Suggestion> suggestions = new ArrayList<Suggestion>();

        suggestions.add(OPEN_ACCOUNT_OPTION.getSuggestedReply());
        suggestions.add(CHECK_BALANCE_OPTION.getSuggestedReply());
        suggestions.add(TRANSFER_MONEY_OPTION.getSuggestedReply());

        return suggestions;
    }
}
// [END of the chat bot for the Banking Agent]