/*
 * Copyright 2017-present Facebook, Inc.
 *
 * 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.facebook.buck.util.trace.uploader;

import com.facebook.buck.util.BestCompressionGZIPOutputStream;
import com.facebook.buck.util.network.MacIpv6BugWorkaround;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Stopwatch;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter; // NOPMD this is just a log
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.concurrent.TimeUnit;
import okhttp3.HttpUrl;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import org.kohsuke.args4j.CmdLineException;
import org.kohsuke.args4j.CmdLineParser;
import org.kohsuke.args4j.Option;

public final class Main {
  @Option(name = "--buildId", required = true)
  private String uuid;

  @Option(name = "--traceFilePath", required = true)
  private Path traceFilePath;

  @Option(name = "--traceFileKind", required = true)
  private String traceFileKind;

  @Option(name = "--baseUrl", required = true)
  private URI baseUrl;

  @Option(name = "--log", required = true)
  private File logFile;

  private PrintWriter log;

  static {
    MacIpv6BugWorkaround.apply();
  }

  public static void main(String[] args) throws IOException {
    Main main = new Main();
    CmdLineParser parser = new CmdLineParser(main);
    try {
      parser.parseArgument(args);
      main.run();
    } catch (CmdLineException e) {
      System.err.println(e.getMessage());
      parser.printUsage(System.err);
    }
  }

  private void run() throws IOException {
    log = new PrintWriter(logFile); // NOPMD this is just a log
    try {
      upload();
    } finally {
      log.close();
    }
  }

  private void upload() {
    Stopwatch timer = Stopwatch.createStarted();
    try {
      OkHttpClient client = new OkHttpClient();
      HttpUrl url =
          HttpUrl.get(baseUrl)
              .newBuilder()
              .addQueryParameter("uuid", this.uuid)
              .addQueryParameter("trace_file_kind", this.traceFileKind)
              .build();
      Path compressedTracePath = gzip(traceFilePath);

      log.format("Build ID: %s\n", uuid);
      log.format("Trace file: %s (%d) bytes\n", traceFilePath, Files.size(traceFilePath));
      log.format("Compressed size: %d bytes\n", Files.size(compressedTracePath));
      log.format("Upload URL: %s\n", url);
      log.format("Uploading compressed trace...");
      Request request =
          new Request.Builder()
              .url(url)
              .post(
                  new MultipartBody.Builder()
                      .setType(MultipartBody.FORM)
                      .addFormDataPart(
                          "trace_file",
                          traceFilePath.getFileName().toString() + ".gz",
                          RequestBody.create(
                              MediaType.parse("application/json+gzip"),
                              compressedTracePath.toFile()))
                      .build())
              .build();

      try (Response response = client.newCall(request).execute()) {
        ObjectMapper objectMapper = new ObjectMapper();
        JsonNode root = objectMapper.readTree(response.body().byteStream());

        if (root.get("success").asBoolean()) {
          log.format("Success!\nFind it at %s\n", root.get("content").get("uri").asText());
        } else {
          log.format("Failed!\nMessage: %s\n", root.get("error").asText());
        }
      }
    } catch (Exception e) {
      log.format("\nFailed to upload trace; %s\n", e.getMessage());
    } finally {
      log.format("Elapsed time: %d millis", timer.elapsed(TimeUnit.MILLISECONDS));
    }
  }

  private Path gzip(Path uncompressed) throws IOException {
    Path compressed = Files.createTempFile("tmp", ".gz");
    try (BestCompressionGZIPOutputStream gzipStream =
        new BestCompressionGZIPOutputStream(
            new BufferedOutputStream(Files.newOutputStream(compressed)), true)) {
      Files.copy(uncompressed, gzipStream);
    }
    return compressed;
  }
}

@@ -2154,17 +2154,61 @@
     
-main.run(
+int exitCode = main.run();%0A      System.exit(exitCode
 );%0A 
@@ -2320,16 +2320,38 @@
 m.err);%0A
+      System.exit(1);%0A
     %7D%0A  
@@ -2363,20 +2363,19 @@
 private 
-void
+int
  run() t
@@ -2474,16 +2474,23 @@
 %7B%0A      
+return 
 upload()
@@ -2547,20 +2547,19 @@
 private 
-void
+int
  upload(
@@ -4181,32 +4181,52 @@
 ri%22).asText());%0A
+          return 0;%0A
         %7D else %7B
@@ -4226,16 +4226,16 @@
  else %7B%0A
-
         
@@ -4294,32 +4294,52 @@
 or%22).asText());%0A
+          return 1;%0A
         %7D%0A      
@@ -4364,24 +4364,24 @@
 eption e) %7B%0A
-
       log.fo
@@ -4432,24 +4432,40 @@
 Message());%0A
+      return 1;%0A
     %7D finall

return non-zero code on error