package com.friday.custody.service.trustService.csc.utils;

import org.apache.commons.lang.StringUtils;
import org.apache.http.Consts;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.config.ConnectionConfig;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.conn.SystemDefaultRoutePlanner;
import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
import org.apache.http.impl.nio.client.HttpAsyncClients;
import org.apache.http.impl.nio.conn.PoolingNHttpClientConnectionManager;
import org.apache.http.impl.nio.reactor.DefaultConnectingIOReactor;
import org.apache.http.nio.reactor.ConnectingIOReactor;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.ssl.TrustStrategy;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import java.io.IOException;
import java.net.ProxySelector;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Map;
import java.util.concurrent.Future;

public class ErpHttpClientHelper
{
  public static final String MIME_JSON = "application/json";
  public static final String MIME_TEXT = "text/plain";
  public static final String MIME_HTML = "text/html";
  public static final String CHARSET_UTF8 = "UTF-8";
  public static int SOCKET_TIMEOUT = 30000*100;
  public static int MAX_CONN = 3000;
  public static int CONNECT_TIMEOUT = 5000*100;
  public static int CONNECTION_REQUEST_TIMEOUT = 5000*100;
  private static final Logger logger = LoggerFactory.getLogger(ErpHttpClientHelper.class);
  private static CloseableHttpAsyncClient asyncclient = null;
  private static ResponseHandler<String> responseHandler = null;
  
  public static synchronized void init()
  {
    closeHttpClient();
    responseHandler = new ResponseHandler()
    {
      public String handleResponse(HttpResponse response)
        throws ClientProtocolException, IOException
      {
        int status = response.getStatusLine().getStatusCode();
        if ((status >= 200) && (status < 300))
        {
          HttpEntity entity = response.getEntity();
          return entity != null ? EntityUtils.toString(entity) : null;
        }
        throw new ClientProtocolException("Unexpected response status: " + status);
      }
    };
    RequestConfig defaultRequestConfig = RequestConfig.custom().setSocketTimeout(SOCKET_TIMEOUT)
      .setConnectTimeout(CONNECT_TIMEOUT).setConnectionRequestTimeout(CONNECTION_REQUEST_TIMEOUT).build();
    try
    {
      SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(new TrustStrategy()
      {
        public boolean isTrusted(X509Certificate[] chain, String authType)
          throws CertificateException
        {
          return true;
        }
      }).build();
      HostnameVerifier v = new HostnameVerifier()
      {
        public boolean verify(String arg0, SSLSession arg1)
        {
          return true;
        }
      };
      ConnectingIOReactor ioReactor = new DefaultConnectingIOReactor();
      
      
      PoolingNHttpClientConnectionManager cm2 = new PoolingNHttpClientConnectionManager(ioReactor);
      ConnectionConfig connectionConfig2 = ConnectionConfig.custom().setCharset(Consts.UTF_8).build();
      cm2.setDefaultConnectionConfig(connectionConfig2);
      cm2.setMaxTotal(MAX_CONN);
      cm2.setDefaultMaxPerRoute(MAX_CONN);
      SystemDefaultRoutePlanner routePlanner = new SystemDefaultRoutePlanner(ProxySelector.getDefault());
      asyncclient = HttpAsyncClients.custom().setDefaultRequestConfig(defaultRequestConfig)
        .setRoutePlanner(routePlanner).setConnectionManager(cm2).build();
      
      asyncclient.start();
    }
    catch (Exception e)
    {
      logger.error("", e);
    }
  }
  
  private static CloseableHttpAsyncClient getAsyncClient()
  {
    if (asyncclient == null) {
      init();
    }
    return asyncclient;
  }
  
  public static void closeHttpClient()
  {
    if (asyncclient != null) {
      try
      {
        asyncclient.close();
      }
      catch (IOException e)
      {
        logger.error("", e);
      }
    }
  }
  
  public static String httpGet(String url, Map<String, String> headers)
    throws Exception
  {
    Future<HttpResponse> ft = getAsyncClient().execute(createGet(url, headers), null);
    HttpResponse res = (HttpResponse)ft.get();
    return (String)responseHandler.handleResponse(res);
  }
  
  public static String httpGet(String url)
    throws Exception
  {
    return httpGet(url, null);
  }
  
  public static String httpPost(String url, String body, Map<String, String> headers)
    throws Exception
  {
    return httpPost(url, body, "text/plain", headers);
  }
  
  public static String httpPost(String url, String body)
    throws Exception
  {
    return httpPost(url, body, "text/plain", null);
  }
  
  public static String httpPost(String url, String body, String mimeType, Map<String, String> headers)
    throws Exception
  {
    Future<HttpResponse> ft = getAsyncClient().execute(createPost(url, body, mimeType, "UTF-8", headers), 
      null);
    HttpResponse res = (HttpResponse)ft.get();
    return (String)responseHandler.handleResponse(res);
  }
  
  public static String httpPost(String url, String body, String mimeType, String charset, Map<String, String> headers)
    throws Exception
  {
    Future<HttpResponse> ft = getAsyncClient().execute(createPost(url, body, mimeType, charset, headers), null);
    HttpResponse res = (HttpResponse)ft.get();
    return (String)responseHandler.handleResponse(res);
  }
  
  public static String httpPost(String url, String body, String mimeType)
    throws Exception
  {
    return httpPost(url, body, mimeType, null);
  }
  
  private static void putHeaders(HttpRequestBase req, Map<String, String> headers)
  {
    if (headers != null) {
      for (Map.Entry<String, String> entry : headers.entrySet()) {
        req.setHeader((String)entry.getKey(), (String)entry.getValue());
      }
    }
  }
  
  private static HttpGet createGet(String url, Map<String, String> headers)
  {
    HttpGet httpget = new HttpGet(url);
    putHeaders(httpget, headers);
    return httpget;
  }
  
  private static HttpPost createPost(String url, String body, String mimeType, String charset, Map<String, String> headers)
  {
    HttpPost httppost = new HttpPost(url);
    putHeaders(httppost, headers);
    if (StringUtils.isNotBlank(body)) {
      httppost.setEntity(new StringEntity(body, ContentType.create(mimeType, charset)));
    }
    return httppost;
  }
  
}
