package com.heroku.sdk.deploy.endpoints;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import com.heroku.api.BuildpackInstallation;
import com.heroku.api.HerokuAPI;
import com.heroku.api.Source;
import com.heroku.sdk.deploy.utils.Curl;
import com.heroku.sdk.deploy.utils.Logger;
import com.heroku.sdk.deploy.utils.RestClient;
import org.apache.commons.text.StringEscapeUtils;

public class Builds extends ApiEndpoint {

  public static final String JVM_BUILDPACK_URL="https://codon-buildpacks.s3.amazonaws.com/buildpacks/heroku/jvm-common.tgz";

  public static final String METRICS_BUILDPACK_URL="https://codon-buildpacks.s3.amazonaws.com/buildpacks/heroku/metrics.tgz";

  public static final String EXEC_BUILDPACK_URL="https://codon-buildpacks.s3.amazonaws.com/buildpacks/heroku/exec.tgz";

  private String blobGetUrl;

  private List<String> buildpackUrls;

  private String commit;

  private HerokuAPI api;

  public Builds(String appName, String client, String commit, String apiKey, List<String> buildpacks) throws IOException {
    super(appName, client, apiKey);
    this.commit = commit;
    this.api = new HerokuAPI(apiKey);

    if (buildpacks == null || buildpacks.isEmpty()) {
      HerokuAPI api = new HerokuAPI(apiKey);
      List<BuildpackInstallation> buildpackInstalls = api.listBuildpackInstallations(appName);

      if (buildpackInstalls.isEmpty()) {
        buildpackUrls = Collections.singletonList(JVM_BUILDPACK_URL);
      } else if (containsJvmBuildpack(buildpackInstalls)) {
        buildpackUrls = new ArrayList<>();
        for (BuildpackInstallation buildpack : buildpackInstalls) {
          buildpackUrls.add(resolveBuildpack(buildpack.getBuildpack().getName()));
        }
      } else {
        throw new IllegalArgumentException("Your buildpacks do not contain the heroku/jvm buildpack!" +
            "Add heroku/jvm to your buildpack configuration or run `heroku buildpacks:clear`.");
      }
    } else {
      buildpackUrls = new ArrayList<>(buildpacks.size());
        for (String buildpack : buildpacks) {
          buildpackUrls.add(resolveBuildpack(buildpack));
        }
    }
  }

  public void upload(File slugFile, Logger listener) throws IOException, InterruptedException {
    if (blobUrl == null) {
      throw new IllegalStateException("Source must be created before uploading!");
    }

    if (useCurl) {
      listener.logDebug("Uploading with curl");
      Curl.put(blobUrl, slugFile);
    } else {
      RestClient.put(blobUrl, slugFile, listener);
    }
  }

  public String getBlobUrl() {
    return blobUrl;
  }

  public Source createSource() throws IOException {
    Source source = this.api.createSource();
    blobUrl = source.getSource_blob().getPut_url();
    blobGetUrl = source.getSource_blob().getGet_url();

    return source;
  }

  public Map build(RestClient.OutputLogger logger) throws IOException, InterruptedException {
    if (blobGetUrl == null) {
      throw new IllegalStateException("Source must be created before releasing!");
    }

    String urlStr = BASE_URL + "/apps/" + appName + "/builds";

    String data = "{"+
        "\"buildpacks\":" + getBuildpacksJson() + ", " +
        "\"source_blob\":{\"url\":\"" + (null == blobGetUrl ? "" : StringEscapeUtils.escapeJson(blobGetUrl)) +
        "\",\"version\":\"" + (null == commit ? "" : StringEscapeUtils.escapeJson(commit)) + "\"}}";

    Map buildResponse = RestClient.post(urlStr, data, headers);

    String outputUrl = (String)buildResponse.get("output_stream_url");
    String buildId = (String)buildResponse.get("id");

    if (outputUrl != null) {
      try {
        RestClient.get(outputUrl, headers, logger);
      } catch (IOException e) {
        logger.log("Deployment output not available. Polling for status...");
      }
      return pollForBuildInfo(buildId);
    } else {
      logger.log("Deployment output not available. Polling for status...");
      return pollForBuildInfo(buildId);
    }
  }

  public Map pollForBuildInfo(String buildId) throws IOException, InterruptedException {
    for (int i = 0; i < 15; i++) {
      Thread.sleep(2000);
      Map info = getBuildInfo(buildId);
      if (!"pending".equals(info.get("status"))) {
        return info;
      }
    }
    return getBuildInfo(buildId);
  }

  public Map getBuildInfo(String buildId) throws IOException {
    String buildStatusUrlStr = BASE_URL + "/apps/" + appName + "/builds/" + buildId;
    return RestClient.get(buildStatusUrlStr, headers);
  }

  private String getBuildpacksJson() {
    StringBuilder buildpacksString = new StringBuilder();
    for (String url : buildpackUrls) {
      buildpacksString.append(",{\"url\":\"").append(StringEscapeUtils.escapeJson(url)).append("\"}");
    }
    return buildpacksString.toString().replaceFirst(",", "[") + "]";
  }

  private Boolean containsJvmBuildpack(List<BuildpackInstallation> buildpackInstalls) {
    for (BuildpackInstallation buildpack : buildpackInstalls) {
      if (buildpack.getBuildpack().getName().startsWith("heroku/jvm")) {
        return true;
      } else if (buildpack.getBuildpack().getName().startsWith("https://github.com/heroku/heroku-buildpack-jvm-common")) {
        return true;
      } else if (buildpack.getBuildpack().getName().startsWith("https://codon-buildpacks.s3.amazonaws.com/buildpacks/heroku/jvm-common.tgz")) {
        return true;
      }
    }
    return false;
  }

  private String resolveBuildpack(String buildpack) {
    if (buildpack.equals("jvm-common")) {
      return JVM_BUILDPACK_URL;
    } else if (buildpack.equals("heroku/jvm")) {
      return JVM_BUILDPACK_URL;
    } else if (buildpack.equals("heroku/metrics")) {
      return METRICS_BUILDPACK_URL;
    } else if (buildpack.equals("heroku/exec")) {
      return EXEC_BUILDPACK_URL;
    } else {
      return buildpack;
    }
  }
}

@@ -569,38 +569,32 @@
 CK_URL=%22https://
-codon-
 buildpacks.s3.am
@@ -578,33 +578,41 @@
 ttps://buildpack
-s
+-registry
 .s3.amazonaws.co
@@ -630,31 +630,24 @@
 s/heroku/jvm
--common
 .tgz%22;%0A%0A  pu
@@ -694,38 +694,32 @@
 CK_URL=%22https://
-codon-
 buildpacks.s3.am
@@ -703,33 +703,41 @@
 ttps://buildpack
-s
+-registry
 .s3.amazonaws.co
@@ -820,38 +820,32 @@
 CK_URL=%22https://
-codon-
 buildpacks.s3.am
@@ -829,33 +829,41 @@
 ttps://buildpack
-s
+-registry
 .s3.amazonaws.co
@@ -5281,32 +5281,138 @@
    return true;%0A
+      %7D else if (buildpack.getBuildpack().getName().startsWith(JVM_BUILDPACK_URL)) %7B%0A        return true;%0A
       %7D else if 

Change codon buildpack URLs to buildpack registry URLs