/**
   * This method is a final check to ensure that user inputs are legitimate. Any situation where the
   * user has entered both inputs in an either/or scenario will fail the build. If the user has left
   * both blank where we need one, it will also fail.
   *
   * @return Boolean sanityCheckPassed
   */
  private Boolean preFlightSanityChecks() {

    // check whether user entered both values for template id/template file
    if (!this.templateID.equals("") && !this.templateFile.equals("")) {
      JenkinsLogger.error(
          "Values were provided for both template ID and file. Please provide just one or the other.");
      return false;
    }

    // check whether we have neither template id nor file
    if (this.templateID.equals("") && this.templateFile.equals("")) {
      JenkinsLogger.error(
          "No value was provided for template ID or file. Please provide either a valid Skytap template ID, or a valid template file.");
      return false;
    }

    // check whether no environment file value was provided
    if (this.configFile.equals("")) {
      JenkinsLogger.error(
          "No value was provided for the environment file. Please provide a valid environment file value.");
      return false;
    }

    return true;
  }
  private String buildCreateConfigRequestURL(String templateId) {

    JenkinsLogger.log("Building request url ...");

    StringBuilder sb = new StringBuilder("https://cloud.skytap.com/");
    sb.append("configurations/");
    sb.append("?template_id=");
    sb.append(templateId);

    JenkinsLogger.log("Request URL: " + sb.toString());
    return sb.toString();
  }
  private String buildCheckTemplateURL(String id) {

    JenkinsLogger.log("Building request url ...");

    StringBuilder sb = new StringBuilder("https://cloud.skytap.com/");
    sb.append("templates/");
    sb.append(id);

    JenkinsLogger.log("Request URL: " + sb.toString());
    return sb.toString();

    // https://cloud.skytap.com/templates/322249

  }
  private String buildUpdateConfigNameRequestURL(
      String newConfigurationId, String newConfigurationName) {

    JenkinsLogger.log("Building request url ...");

    StringBuilder sb = new StringBuilder("https://cloud.skytap.com/");
    sb.append("configurations/");
    sb.append(newConfigurationId);
    sb.append("?name=");
    sb.append(URLEncoder.encode(newConfigurationName));

    JenkinsLogger.log("Request URL: " + sb.toString());
    return sb.toString();

    // https://cloud.skytap.com/configurations/1156812?name=updatedconfigname
  }
  /**
   * This method verifies the busy status of the template to be used. In the event of a busy status,
   * it enters a wait/retry loop.
   *
   * @param tempId
   */
  private Boolean checkIsTemplateAvailable(String tempId) {

    JenkinsLogger.log("Checking availability of template with id: " + tempId);

    // build busy check request
    String requestURL = buildCheckTemplateURL(tempId);
    HttpGet hg = SkytapUtils.buildHttpGetRequest(requestURL, this.authCredentials);

    // repeatedly execute request until template is not busy
    String httpRespBody = "";
    Boolean templateIsAvailable = false;

    try {

      int numPollingAttempts = 0;

      while (!templateIsAvailable && (numPollingAttempts < this.NUMBER_OF_RETRIES)) {

        httpRespBody = SkytapUtils.executeHttpRequest(hg);

        // get json object from the response
        JsonParser parser = new JsonParser();
        JsonElement je = parser.parse(httpRespBody);
        JsonObject jo = je.getAsJsonObject();

        // get busy status - if busy in returned
        // JSON array is 'null', means template is not busy

        if (jo.get("busy").isJsonNull()) {
          templateIsAvailable = true;
          JenkinsLogger.log("Template is available.");
        } else {
          templateIsAvailable = false;
          JenkinsLogger.log("Template is busy.");

          // wait before trying again
          int sleepTime = this.RETRY_INTERVAL_SECONDS;
          JenkinsLogger.log("Sleeping for " + sleepTime + " seconds.");
          Thread.sleep(sleepTime * 1000);
        }

        numPollingAttempts++;
      }

      return templateIsAvailable;

    } catch (SkytapException ex) {
      JenkinsLogger.error("Request returned an error: " + ex.getError());
      JenkinsLogger.error("Failing build step.");
      return false;
    } catch (InterruptedException e1) {
      JenkinsLogger.error(e1.getMessage());
      return false;
    }
  }
  public Boolean executeStep(AbstractBuild build, SkytapGlobalVariables globalVars) {

    JenkinsLogger.defaultLogMessage("----------------------------------------");
    JenkinsLogger.defaultLogMessage("Creating Environment from Template");
    JenkinsLogger.defaultLogMessage("----------------------------------------");

    if (preFlightSanityChecks() == false) {
      return false;
    }

    this.globalVars = globalVars;
    this.authCredentials = SkytapUtils.getAuthCredentials(build);

    String expTemplateFile = SkytapUtils.expandEnvVars(build, templateFile);
    String expConfigFile = SkytapUtils.expandEnvVars(build, configFile);
    String expConfigName = SkytapUtils.expandEnvVars(build, configName);

    // if user has provided just a filename with no path, default to
    // place it in their Jenkins workspace

    if (!expTemplateFile.equals("")) {
      expTemplateFile = SkytapUtils.convertFileNameToFullPath(build, expTemplateFile);
    }

    if (!expConfigFile.equals("")) {
      expConfigFile = SkytapUtils.convertFileNameToFullPath(build, expConfigFile);
    }

    JenkinsLogger.log("Template File: " + expTemplateFile);
    JenkinsLogger.log("Config File: " + expConfigFile);
    JenkinsLogger.log("Config Name: " + expConfigName);

    try {
      runtimeTemplateID = SkytapUtils.getRuntimeId(templateID, expTemplateFile);
    } catch (FileNotFoundException e1) {
      JenkinsLogger.error("Error obtaining template id: " + e1.getMessage());
      return false;
    }

    JenkinsLogger.log("Template ID:  " + runtimeTemplateID);

    // check busy state of template - if template doesn't become
    // available after configured wait time, fail build step
    if (checkIsTemplateAvailable(runtimeTemplateID) == false) {
      JenkinsLogger.error(
          "Template ID: "
              + runtimeTemplateID
              + " has not become available yet. Failing build step.");
      return false;
    }

    // build request url
    String requestURL = buildCreateConfigRequestURL(runtimeTemplateID);

    // create request for Skytap API
    HttpPost hp = SkytapUtils.buildHttpPostRequest(requestURL, this.authCredentials);

    // execute request
    String httpRespBody = "";

    try {
      httpRespBody = SkytapUtils.executeHttpRequest(hp);
    } catch (SkytapException e1) {
      JenkinsLogger.error("Skytap Exception: " + e1.getMessage());
      return false;
    }

    try {
      SkytapUtils.checkResponseForErrors(httpRespBody);
    } catch (SkytapException ex) {
      JenkinsLogger.error("Request returned an error: " + ex.getError());
      JenkinsLogger.error("Failing build step.");
      return false;
    }

    // get json object from the response
    JsonParser parser = new JsonParser();
    JsonElement je = parser.parse(httpRespBody);
    JsonObject jo = je.getAsJsonObject();

    // TODO: extract into separate method
    // if name was provided, update the created environment's name
    // using an http put request
    if (!expConfigName.equals("")) {

      String configId = jo.get("id").getAsString();

      // build request url
      String updateRequestURL = buildUpdateConfigNameRequestURL(configId, expConfigName);

      // create request for Skytap API
      HttpPut hput = SkytapUtils.buildHttpPutRequest(updateRequestURL, this.authCredentials);

      // execute request
      httpRespBody = "";

      try {
        httpRespBody = SkytapUtils.executeHttpRequest(hput);
      } catch (SkytapException ex) {
        JenkinsLogger.error("Request returned an error: " + ex.getError());
        JenkinsLogger.error("Failing build step.");
        return false;
      }

      try {
        SkytapUtils.checkResponseForErrors(httpRespBody);
      } catch (SkytapException ex) {
        JenkinsLogger.error("Request returned an error: " + ex.getError());
        JenkinsLogger.error("Failing build step.");
        return false;
      }

      // if the update succeeded, update the json object also
      je = parser.parse(httpRespBody);
      jo = je.getAsJsonObject();
    }

    // save json object to the environment file path
    // if user has provided just a filename with no path, default to
    // place it in their Jenkins workspace
    expConfigFile = SkytapUtils.convertFileNameToFullPath(build, expConfigFile);

    Writer output = null;
    File file = new File(expConfigFile);
    try {

      output = new BufferedWriter(new FileWriter(file));
      output.write(httpRespBody);
      output.close();
    } catch (IOException e) {

      JenkinsLogger.error("Skytap Plugin failed to save environment to file: " + expConfigFile);
      return false;
    }

    // Sleep for a a few seconds to make sure the Config is stable, then we
    // can exit
    try {
      Thread.sleep(10000);
    } catch (InterruptedException e) {
      JenkinsLogger.error("Error: " + e.getMessage());
    }

    JenkinsLogger.defaultLogMessage(
        "Environment successfully created and saved to file: " + expConfigFile);
    JenkinsLogger.defaultLogMessage("----------------------------------------");
    return true;
  }