
package eu.dnetlib.doiboost.orcid;

import java.io.IOException;
import java.net.ConnectException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import org.apache.commons.io.IOUtils;
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.impl.conn.PoolingHttpClientConnectionManager;
import org.junit.jupiter.api.Test;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

public class ElasticSearchTest {

	private static final String BASE_CFG_URL = "http://%s:9200/%s/%s/%s?pretty";

	private String indexHost;
	private String indexName;
	private String indexType;
	private String record;
	private int readTimeout = 30000;

	private int nThreads = 4;
	private int nTasks = 150;
	private ExecutorService executorService = Executors.newFixedThreadPool(nThreads);
	private List<Future<Integer>> resList = Lists.newArrayList();

	public void setup() {
		indexHost = "ip-90-147-167-25.ct1.garrservices.it";
		indexName = "orcid_update";
		indexType = "record";
		record = "{\n"
			+ "          \"timestamp\": 1540825815212,\n"
			+ "          \"pid\": \"0000-0002-5214-479X\",\n"
			+ "          \"blob\": \"[encoded data, 17208 characters]\"\n"
			+ "        }";
	}

	// @Test
	public void testSingleFeed() throws Exception {
		setup();
		String url = String.format(BASE_CFG_URL, indexHost, indexName, indexType, "0000-0002-5214-479X");
		System.out.println("Sending to: " + url);
		System.out.println("record: " + record);

		System.out.println("response from ES: " + getResponse(url, record));
	}

//  @Test
	public void testMultipleFeed() throws Exception {
		setup();
		String recordTemplate = "{\n"
			+ "          \"timestamp\": 1540825815212,\n"
			+ "          \"pid\": \"0000-0000-0000-%s\",\n"
			+ "          \"blob\": \"[encoded data, 17208 characters]\"\n"
			+ "        }";
		for (int i = 0; i < 10000; i++) {
			String orcidId = String.format("0000-0000-0000-%s", Integer.toString(i));
			String url = String.format(BASE_CFG_URL, indexHost, indexName, indexType, orcidId);
			String recordTs = String.format(recordTemplate, Integer.toString(i));
			getResponse(url, recordTs);
		}
		System.out.println("");
	}

	private String getResponse(final String url, final String json) {
		CloseableHttpClient client = HttpClients.createDefault();
		try {

			HttpPost httpPost = new HttpPost(url);
			if (json != null) {
				StringEntity entity = new StringEntity(json);
				httpPost.setEntity(entity);
				httpPost.setHeader("Accept", "application/json");
				httpPost.setHeader("Content-type", "application/json");
			}
			CloseableHttpResponse response = client.execute(httpPost);

			return IOUtils.toString(response.getEntity().getContent());
		} catch (Throwable e) {
			throw new RuntimeException("Error on executing request ", e);
		} finally {
			try {
				client.close();
			} catch (IOException e) {
				throw new RuntimeException("Unable to close client ", e);
			}
		}
	}

//	@Test
	public void testMultiThreadFeed() throws Exception {
		setup();
		int countAll = 0;
		int countOk = 0;
		int partial = 0;
		String recordTemplate = "{\n"
			+ "          \"timestamp\": 1540825815212,\n"
			+ "          \"pid\": \"%s\",\n"
			+ "          \"blob\": \"[encoded data, 17208 characters]\"\n"
			+ "        }";
		Map<Integer, Integer> errors = Maps.newHashMap();
		PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
		cm.setMaxTotal(nThreads);
		CloseableHttpClient client = HttpClients.custom().setConnectionManager(cm).build();
		for (int i = 40000; i < 41000; i++) {
			String orcidId = String.format("%s", i);
			String record = String.format(recordTemplate, orcidId);
			countAll++;
			if (partial == nTasks) {
				System.out
					.println(
						"Waiting for tasks to complete before resubmitting to executor (countAll = " + countAll
							+ ") . . . ");
				System.out.println("Getting replies");
				long startWait = System.currentTimeMillis();
				for (Future<Integer> res : resList) {
					if (res.get() == 200)
						countOk++;
				}
				resList.clear();
				partial = 0;
				System.out
					.println(". . . Ready to submit again after " + (System.currentTimeMillis() - startWait) + " ms");
			}
			partial++;
			Future<Integer> res = executorService.submit(() -> {
				CloseableHttpResponse responsePPOST = null;
				try {

					String url = String.format(BASE_CFG_URL, indexHost, indexName, indexType, orcidId);
					HttpPost post = new HttpPost(url);
					post.setHeader("Accept", "application/json");
					post.setHeader("Content-type", "application/json");
					StringEntity entity = new StringEntity(record);
					post.setEntity(entity);
					responsePPOST = client.execute(post);
					int statusCode = responsePPOST.getStatusLine().getStatusCode();
					switch (statusCode) {
						case 200:
						case 201:
							return statusCode;
						default:
							System.out
								.println(
									responsePPOST.getStatusLine().getStatusCode() + ": "
										+ responsePPOST.getStatusLine().getReasonPhrase());
							System.out.println("Source record causing error: " + record);
							errors.merge(statusCode, 1, Integer::sum);
							return statusCode;
					}
				} catch (ConnectException ce) {
					throw ce;
				} catch (IOException e) {
					e.printStackTrace();
					errors.merge(-1, 1, Integer::sum);
				} finally {
					if (responsePPOST != null)
						responsePPOST.close();
				}
				return -1;
			});
			resList.add(res);
		}
		executorService.shutdown();

		// now let's wait for the results. We can block ourselves here: we have nothing else to do
		System.out.println("Waiting for responses");
		for (Future<Integer> res : resList) {
			if (res.get() == 200)
				countOk++;
		}
		client.close();
		cm.shutdown();

		System.out.println("countOk: " + countOk);
		System.out.println("countAll: " + countAll);
		System.out.println("errors count: " + errors.size());
	}
}
