package com.conveyal.taui.controllers;

import com.conveyal.r5.analyst.error.TaskError;
import com.conveyal.r5.common.JsonUtilities;
import com.conveyal.taui.util.HttpUtil;
import com.conveyal.taui.AnalystConfig;
import com.google.common.io.ByteStreams;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import spark.Request;
import spark.Response;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;

import static spark.Spark.*;

/**
 * Handles talking to the broker.
 */
public class SinglePointAnalysisController {
    public static final Logger LOG = LoggerFactory.getLogger(SinglePointAnalysisController.class);

    public static byte[] analysis (Request req, Response response) throws IOException {
        // we already know the user is authenticated, and we need not check if they have access to the graphs etc,
        // as they're all coded with UUIDs which contain significantly more entropy than any human's account password.
        String path = req.pathInfo().replaceAll("^/api/analysis/", "");
        String method = req.requestMethod();

        String brokerUrl = AnalystConfig.offline ? "http://localhost:6001" : AnalystConfig.brokerUrl;

        CloseableHttpResponse brokerRes = null;
        InputStream brokerInputStream = null;
        try {
            if ("GET".equals(method)) {
                HttpGet get = new HttpGet(brokerUrl + "/" + path);
                brokerRes = HttpUtil.httpClient.execute(get);
            } else if ("POST".equals(method)) {
                HttpPost post = new HttpPost(brokerUrl + "/" + path);
                // We're ignoring the content type of the incoming request and forcing it to JSON
                // which should be fine since the broker's single point endpoint always expects POST bodies to be JSON.
                // We do need to force the encoding to utf-8 here otherwise multi-byte characters get corrupted.
                post.setEntity(new StringEntity(req.body(), ContentType.create("application/json", "utf-8")));
                brokerRes = HttpUtil.httpClient.execute(post);
            } else if ("DELETE".equals(method)) {
                HttpDelete delete = new HttpDelete(brokerUrl + "/" + path);
                brokerRes = HttpUtil.httpClient.execute(delete);
            } else {
                throw new RuntimeException("Unsupported HTTP method on request, not proxying to the broker.");
            }
            response.status(brokerRes.getStatusLine().getStatusCode());
            response.type(brokerRes.getFirstHeader("Content-Type").getValue());
            // TODO set encoding?
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            brokerInputStream = new BufferedInputStream(brokerRes.getEntity().getContent());
            long l = ByteStreams.copy(brokerInputStream, baos);
            LOG.info("Returning {} bytes to scenario editor frontend", l);
            brokerInputStream.close();
            EntityUtils.consume(brokerRes.getEntity());
            return baos.toByteArray();
        } catch (Exception exception) {
            LOG.error("Uncaught exception: ", exception.toString());
            exception.printStackTrace();
            response.status(500);
            response.type("application/json");
            List<TaskError> taskErrors = Arrays.asList(new TaskError(exception));
            response.body(new String(JsonUtilities.objectToJsonBytes(taskErrors)));
            // TODO should we just return the byte array here rather than calling body()? Does returning null below override that?
        } finally {
            if (brokerRes != null) brokerRes.close();
            if (brokerInputStream != null) brokerInputStream.close();
        }
        return null;
    }

    public static void register () {
        // TODO is there a way to do a wildcard that includes slashes?
        // Also, are there any broker endpoints that don't have two path components.
        post("/api/analysis/*/*", SinglePointAnalysisController::analysis);
        get("/api/analysis/*/*", SinglePointAnalysisController::analysis);
        delete("/api/analysis/*/*", SinglePointAnalysisController::analysis);
    }
}

@@ -1140,21 +1140,16 @@
 onse res
-ponse
 ) throws
@@ -1670,53 +1670,8 @@
 ll;%0A
-        InputStream brokerInputStream = null;
 %0A   
@@ -2817,37 +2817,32 @@
 %0A            res
-ponse
 .status(brokerRe
@@ -2888,29 +2888,24 @@
          res
-ponse
 .type(broker
@@ -3060,38 +3060,32 @@
 ();%0A            
-broker
 InputStream = ne
@@ -3080,16 +3080,19 @@
 tStream 
+is 
 = new Bu
@@ -3142,24 +3142,46 @@
 Content());%0A
+            try %7B%0A    
             
@@ -3206,33 +3206,18 @@
 ms.copy(
-brokerInputStream
+is
 , baos);
@@ -3213,24 +3213,28 @@
 (is, baos);%0A
+    
             
@@ -3308,33 +3308,22 @@
         
-brokerInputStream
+    is
 .close()
@@ -3316,32 +3316,36 @@
     is.close();%0A
+    
             Enti
@@ -3400,79 +3400,389 @@
     
-return baos.toByteArray();%0A        %7D catch (Exception exception) %7B%0A    
+%7D catch (Exception e) %7B%0A                reportException(res, e);%0A            %7D%0A            return baos.toByteArray();%0A        %7D catch (Exception e) %7B%0A            reportException(res, e);%0A        %7D finally %7B%0A            if (brokerRes != null) brokerRes.close();%0A        %7D%0A        return null;%0A    %7D%0A%0A    public static void reportException (Response response, Exception exception) %7B%0A
     
@@ -3838,28 +3838,24 @@
 oString());%0A
-    
         exce
@@ -3879,36 +3879,32 @@
 race();%0A        
-    
 response.status(
@@ -3909,36 +3909,32 @@
 s(500);%0A        
- 
-   
 response.type(%22a
@@ -3960,20 +3960,16 @@
         
-   
- 
 List%3CTas
@@ -4026,28 +4026,24 @@
 xception));%0A
-    
         resp
@@ -4114,314 +4114,8 @@
 ));%0A
-            // TODO should we just return the byte array here rather than calling body()? Does returning null below override that?%0A        %7D finally %7B%0A            if (brokerRes != null) brokerRes.close();%0A            if (brokerInputStream != null) brokerInputStream.close();%0A        %7D%0A        return null;%0A
     

Revert "simplify single-point analysis exception handling"