/*
 * 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.servlets;

// [START callback servlet]

// [START import_libraries]
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.rbm.samples.BankingAgentBot;
import com.google.rbm.samples.lib.RbmApiHelper;

import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Base64;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
import java.util.stream.Collectors;
// [END import_libraries]

/**
 * Servlet for Pub/Sub push request callback.
 *
 * JSON data is posted to /callback, parsed and passed to the Banking Agent bot message handler.
 */
@WebServlet(name = "BankingAgentCallback", value = "/callback")
public class BankingAgentCallback extends HttpServlet {
    private static final Logger logger = Logger.getLogger(BankingAgentCallback.class.getName());

    private ScheduledExecutorService backgroundExecutor;

    public BankingAgentCallback() {
        super();
    }

    @Override
    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws IOException {
        logger.info("CALLBACK doPost");

        // check to see that this is a post
        if ("POST".equalsIgnoreCase(request.getMethod())) {
            // read the JSON data from the post body
            String jsonResponse = request.getReader().lines().collect(
                    Collectors.joining(System.lineSeparator())
            );

            logger.info(jsonResponse);

            // load the JSON string into a Json parser
            JsonParser parser = new JsonParser();
            JsonObject obj = parser.parse(jsonResponse).getAsJsonObject();

            // URL validation check
            if (obj.has("secret")) { 
                response.getWriter().println("{\"secret\": \"" + obj.get("secret").toString() + "\"}");
                response.getWriter().flush();
            }
            else if (obj.has("message")) { // make sure there is a message parameter
                obj = obj.get("message").getAsJsonObject();

                // check to see that the message parameter has data
                if (obj.has("data")) {
                    // get the base64 encodded response
                    String encodedResponse = obj.get("data").getAsString();

                    // decode the response
                    byte decodedResponse[] = Base64.getDecoder().decode(encodedResponse);

                    // convert the decoded bytes into a string
                    String decodedJsonResponse = new String(decodedResponse, "UTF-8");

                    // convert JSON response into an object
                    obj = parser.parse(decodedJsonResponse).getAsJsonObject();

                    logger.info(obj.toString());

                    // make sure the Json object contains response text, either plaintext
                    // or a suggested response object
                    if (obj.has("text") || obj.has("suggestionResponse")) {
                        logger.info("handling response");

                        String userResponseText = "";
                        if (obj.has("text")) {
                            userResponseText = obj.get("text").getAsString();
                        }

                        if (obj.has("suggestionResponse")) {
                            userResponseText = obj
                                    .get("suggestionResponse")
                                    .getAsJsonObject()
                                    .get("postbackData")
                                    .getAsString();
                        }

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

                        if (userResponseText.length() > 0) {
                            String senderPhoneNumber = obj.get("senderPhoneNumber").getAsString();
                            String messageId = obj.get("messageId").getAsString();

                            // create an instance of the RBM API helper
                            RbmApiHelper rbmApiHelper = new RbmApiHelper();

                            // let the user know we received and read the message
                            rbmApiHelper.sendReadMessage(messageId, senderPhoneNumber);

                            // create instance of the bot
                            final BankingAgentBot bankingAgentBot
                                    = new BankingAgentBot(senderPhoneNumber);

                            final String responseForBackgroundThread = userResponseText;

                            // check to see if we are in live chat mode, if not, process asynchronously
                            if(!bankingAgentBot.isDirectMessage(userResponseText)) {
                                // create a background thread to run the banking bot, this way
                                // the process of handling the response is non-blocking
                                backgroundExecutor = Executors.newSingleThreadScheduledExecutor();
                                backgroundExecutor.schedule(new Runnable() {
                                    @Override
                                    public void run() {
                                        try {
                                            // forward the response to the bot handler
                                            bankingAgentBot.handleResponse(responseForBackgroundThread);
                                        } catch (IOException e) {
                                            e.printStackTrace();
                                        }
                                    }
                                }, 0, TimeUnit.HOURS);
                            }
                            else {
                                // live chat mode, handle directly
                                bankingAgentBot.handleAgentDirectMessage(responseForBackgroundThread);
                            }

                            logger.info("Callback complete");
                        }
                    }
                }
            }
        }
    }
}
// [END callback servlet]