// Copyright (C) 2017 The Android Open Source Project
//
// 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.googlesource.gerrit.plugins.its.phabricator.conduit;

import com.google.common.flogger.FluentLogger;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.googlesource.gerrit.plugins.its.phabricator.conduit.results.CallCapsule;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;

/** Abstracts the connection to Conduit API */
class ConduitConnection {
  private static final FluentLogger logger = FluentLogger.forEnclosingClass();

  private final String apiUrlBase;
  private final Gson gson;

  private CloseableHttpClient client;

  ConduitConnection(final String baseUrl) {
    apiUrlBase = baseUrl.replaceAll("/+$", "") + "/api/";
    gson = new Gson();
    client = null;
  }

  /**
   * Gives a cached HttpClient
   *
   * <p>If no cached HttpClient exists, a new one is spawned.
   *
   * @return the cached CloseableHttpClient
   */
  private CloseableHttpClient getClient() {
    if (client == null) {
      logger.atFinest().log("Creating new client connection");
      client = HttpClients.createDefault();
    }
    return client;
  }

  /**
   * Call the given Conduit method without parameters
   *
   * @param method The name of the method that should get called
   * @return The call's result, if there has been no error
   * @throws ConduitException
   */
  JsonElement call(String method, String token) throws ConduitException {
    return call(method, new HashMap<String, Object>(), token);
  }

  /**
   * Calls a conduit method with some parameters
   *
   * @param method The name of the method that should get called
   * @param params A map of parameters to pass to the call
   * @return The call's result, if there has been no error
   * @throws ConduitException
   */
  JsonElement call(String method, Map<String, Object> params, String token)
      throws ConduitException {
    String methodUrl = apiUrlBase + method;

    HttpPost httppost = new HttpPost(methodUrl);

    if (token != null) {
      Map<String, Object> conduitParams = new HashMap<>();
      conduitParams.put("token", token);
      params.put("__conduit__", conduitParams);
    }

    String json = gson.toJson(params);

    logger.atFinest().log("Calling phabricator method %s with the parameters %s", method, json);
    httppost.setEntity(new StringEntity("params=" + json, StandardCharsets.UTF_8));

    CloseableHttpResponse response;
    try {
      response = getClient().execute(httppost);
    } catch (IOException e) {
      throw new ConduitException("Could not execute Phabricator API call", e);
    }
    try {
      logger.atFinest().log("Phabricator HTTP response status: %s", response.getStatusLine());
      HttpEntity entity = response.getEntity();
      String entityString;
      try {
        entityString = EntityUtils.toString(entity);
      } catch (IOException e) {
        throw new ConduitException("Could not read the API response", e);
      }

      logger.atFinest().log("Phabricator response: %s", entityString);
      CallCapsule callCapsule = gson.fromJson(entityString, CallCapsule.class);
      logger.atFinest().log("callCapsule.result: %s", callCapsule.getResult());
      logger.atFinest().log("callCapsule.error_code: %s", callCapsule.getErrorCode());
      logger.atFinest().log("callCapsule.error_info: %s", callCapsule.getErrorInfo());
      if (callCapsule.getErrorCode() != null || callCapsule.getErrorInfo() != null) {
        throw new ConduitErrorException(
            method, callCapsule.getErrorCode(), callCapsule.getErrorInfo());
      }
      return callCapsule.getResult();
    } finally {
      try {
        response.close();
      } catch (IOException e) {
        throw new ConduitException("Could not close API response", e);
      }
    }
  }
}

@@ -3341,16 +3341,21 @@
 );%0A%0A    
+try (
 Closeabl
@@ -3372,34 +3372,8 @@
 nse 
-response;%0A    try %7B%0A      
 resp
@@ -3412,132 +3412,9 @@
 ost)
-;%0A    %7D catch (IOException e) %7B%0A      throw new ConduitException(%22Could not execute Phabricator API call%22, e);%0A    %7D%0A    try
+)
  %7B%0A 
@@ -4426,64 +4426,8 @@
    %7D
- finally %7B%0A      try %7B%0A        response.close();%0A      %7D
  cat
@@ -4439,34 +4439,32 @@
 OException e) %7B%0A
-  
       throw new 
@@ -4495,40 +4495,42 @@
 not 
-close API response%22, e);%0A      %7D
+execute Phabricator API call%22, e);
 %0A   

Convert try to try-with-resource to silence Eclipse warning