package ro.szzsa.utils.connector;

import org.apache.commons.io.IOUtils;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpStatus;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicHeader;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.util.EntityUtils;

import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.util.Collections;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;

import ro.szzsa.utils.connector.exception.ConnectorException;
import ro.szzsa.utils.connector.log.Logger;
import ro.szzsa.utils.connector.log.Loggers;

public class HttpConnector implements Connector {

  private int socketTimeout = 5000;

  private int connectionTimeout = 3000;

  private int numberOfRetries = 4;

  private Logger logger = Loggers.createEmptyLogger();

  private String username;

  private String password;

  @Override
  public String sendRequest(Request request) throws ConnectorException {
    int currentRetries = 0;
    Throwable exception = null;
    logger.debug("|---> Sending request to " + request.getUrl() + "\n" +
                 (request.getMessage() == null ? "" : request.getMessage()));
    while (currentRetries <= numberOfRetries) {
      try (CloseableHttpClient httpclient = buildHttpClient(request.getUrl().startsWith("https"));
           CloseableHttpResponse httpResponse = httpclient.execute(buildHttpPost(request, connectionTimeout))) {
        int statusCode = httpResponse.getStatusLine().getStatusCode();
        if (HttpStatus.SC_OK == statusCode) {
          String response = getResponseMessage(httpResponse.getEntity());
          logger.debug("|<--- Received response from " + request.getUrl() + "\n" + response);
          return response;
        } else {
          throw new ConnectorException("Status code: " + statusCode);
        }
      } catch (KeyStoreException | NoSuchAlgorithmException | KeyManagementException | IOException e) {
        currentRetries++;
        logger.warn("Cannot connect to " + request.getUrl());
        exception = e;
        if (currentRetries <= numberOfRetries) {
          logger.debug("retrying...");
        }
      }
    }
    throw new ConnectorException("Cannot connect to " + request.getUrl(), exception);
  }

  private CloseableHttpClient buildHttpClient(boolean isSecure)
      throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
    HttpClientBuilder httpClientBuilder = HttpClients.custom();
    Header contentTypeHeader = new BasicHeader("content-type", "application/json; charset=utf-8");
    httpClientBuilder.setDefaultHeaders(Collections.singleton(contentTypeHeader));

    if (isSecure) {
      SSLContext sslContext = new SSLContextBuilder()
          .loadTrustMaterial((TrustStrategy) (chain, authType) -> true)
          .build();
      HostnameVerifier hostnameVerifier = (s, sslSession) -> true;
      SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(sslContext, hostnameVerifier);
      httpClientBuilder.setSSLSocketFactory(socketFactory);
    }

    if (username != null) {
      CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
      credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(username, password));
      httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
    }

    return httpClientBuilder.build();
  }

  private String getResponseMessage(HttpEntity entity) throws ConnectorException {
    String response;
    try (InputStream inputStream = entity.getContent()) {
      StringWriter writer = new StringWriter();
      IOUtils.copy(inputStream, writer, Charset.forName("utf-8"));
      response = writer.toString();
      EntityUtils.consume(entity);
    } catch (IOException e) {
      throw new ConnectorException(e);
    }
    return response;
  }

  private HttpPost buildHttpPost(Request request, int connectionTimeout) throws UnsupportedEncodingException {
    HttpPost httpPost = new HttpPost(request.getUrl());
    RequestConfig config = RequestConfig.custom()
        .setSocketTimeout(socketTimeout)
        .setConnectTimeout(connectionTimeout)
        .build();
    httpPost.setConfig(config);
    if (request.getMessage() != null) {
      httpPost.setEntity(new StringEntity(request.getMessage()));
    }
    return httpPost;
  }

  public void setSocketTimeout(int socketTimeout) {
    this.socketTimeout = socketTimeout;
  }

  public void setConnectionTimeout(int connectionTimeout) {
    this.connectionTimeout = connectionTimeout;
  }

  public void setNumberOfRetries(int numberOfRetries) {
    this.numberOfRetries = numberOfRetries;
  }

  public void setLogger(Logger logger) {
    this.logger = logger;
  }

  public void setUsername(String username) {
    this.username = username;
  }

  public void setPassword(String password) {
    this.password = password;
  }
}

@@ -28,16 +28,384 @@
 ector;%0A%0A
+import java.io.IOException;%0Aimport java.io.InputStream;%0Aimport java.io.StringWriter;%0Aimport java.io.UnsupportedEncodingException;%0Aimport java.nio.charset.Charset;%0Aimport java.security.KeyManagementException;%0Aimport java.security.KeyStoreException;%0Aimport java.security.NoSuchAlgorithmException;%0Aimport javax.net.ssl.HostnameVerifier;%0Aimport javax.net.ssl.SSLContext;%0A%0A
 import o
@@ -430,24 +430,24 @@
 io.IOUtils;%0A
-
 import org.a
@@ -1349,407 +1349,8 @@
 s;%0A%0A
-import java.io.IOException;%0Aimport java.io.InputStream;%0Aimport java.io.StringWriter;%0Aimport java.io.UnsupportedEncodingException;%0Aimport java.nio.charset.Charset;%0Aimport java.security.KeyManagementException;%0Aimport java.security.KeyStoreException;%0Aimport java.security.NoSuchAlgorithmException;%0Aimport java.util.Collections;%0A%0Aimport javax.net.ssl.HostnameVerifier;%0Aimport javax.net.ssl.SSLContext;%0A%0A
 impo
@@ -1992,25 +1992,16 @@
  %22%5Cn%22 +%0A
-         
         
@@ -2057,24 +2057,24 @@
 essage()));%0A
+
     while (c
@@ -3363,191 +3363,8 @@
 ();%0A
-    Header contentTypeHeader = new BasicHeader(%22content-type%22, %22application/json; charset=utf-8%22);%0A    httpClientBuilder.setDefaultHeaders(Collections.singleton(contentTypeHeader));%0A%0A
     
@@ -4666,32 +4666,32 @@
 dingException %7B%0A
-
     HttpPost htt
@@ -4726,24 +4726,166 @@
 .getUrl());%0A
+    Header contentTypeHeader = new BasicHeader(%22content-type%22, %22application/json; charset=utf-8%22);%0A    httpPost.setHeader(contentTypeHeader);%0A
     RequestC

Fix content type header