package org.stagemonitor.core.grafana;

import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ThreadPoolExecutor;

import com.fasterxml.jackson.databind.node.ObjectNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.stagemonitor.core.CorePlugin;
import org.stagemonitor.core.util.ExecutorUtils;
import org.stagemonitor.core.util.HttpClient;
import org.stagemonitor.core.util.IOUtils;
import org.stagemonitor.core.util.JsonUtils;
import org.stagemonitor.core.util.StringUtils;

/**
 * Utility class for interacting with the Grafana HTTP API
 * <p/>
 * http://docs.grafana.org/reference/http_api/
 */
public class GrafanaClient {

	private final Logger logger = LoggerFactory.getLogger(getClass());

	private final ThreadPoolExecutor asyncRestPool;

	private final CorePlugin corePlugin;
	private final HttpClient httpClient;

	public GrafanaClient(CorePlugin corePlugin) {
		this(corePlugin, new HttpClient());
	}

	public GrafanaClient(CorePlugin corePlugin, HttpClient httpClient) {
		this.corePlugin = corePlugin;
		this.httpClient = httpClient;
		asyncRestPool = ExecutorUtils
				.createSingleThreadDeamonPool("async-grafana", corePlugin.getThreadPoolQueueCapacityLimit());
	}

	public void createElasticsearchDatasource(final String name, final String url, int reportingInterval) {
		Map<String, Object> dataSource = new HashMap<String, Object>();
		dataSource.put("name", name);
		dataSource.put("url", url);
		dataSource.put("access", "proxy");
		dataSource.put("database", "[stagemonitor-metrics-]YYYY.MM.DD");
		dataSource.put("isDefault", false);
		dataSource.put("type", "elasticsearch");
		dataSource.put("basicAuth", false);
		Map<String, Object> jsonData = new HashMap<String, Object>();
		jsonData.put("timeField", "@timestamp");
		jsonData.put("interval", "Daily");
		jsonData.put("timeInterval", ">" + reportingInterval + "s");
		dataSource.put("jsonData", jsonData);
		asyncGrafanaRequest("POST", "/api/datasources", dataSource);
	}

	/**
	 * Saves a dashboard to Grafana
	 * <p/>
	 * If the Grafana url or the API Key is not configured, this method does nothing.
	 *
	 * @param classPathLocation The location of the dashboard
	 */
	public void sendGrafanaDashboardAsync(final String classPathLocation) {
		try {
			final ObjectNode dashboard = (ObjectNode) JsonUtils.getMapper().readTree(IOUtils.getResourceAsStream(classPathLocation));
			dashboard.put("editable", false);
			final String requestBody = "{\"dashboard\":" + dashboard + ",\"overwrite\": true}";
			asyncGrafanaRequest("POST", "/api/dashboards/db", requestBody);
		} catch (IOException e) {
			logger.warn(e.getMessage(), e);
		}
	}

	private void asyncGrafanaRequest(final String method, final String path, final Object requestBody) {
		final String grafanaUrl = corePlugin.getGrafanaUrl();
		final String grafanaApiToken = corePlugin.getGrafanaApiKey();
		if (isGrafanaConfigured(grafanaUrl, grafanaApiToken)) {
			try {
				asyncRestPool.submit(new Runnable() {
					@Override
					public void run() {
						final Map<String, String> authHeader = Collections.singletonMap("Authorization", "Bearer " + grafanaApiToken);
						httpClient.sendAsJson(method, grafanaUrl + path, requestBody, authHeader);
					}
				});
			} catch (RejectedExecutionException e) {
				ExecutorUtils.logRejectionWarning(e);
			}
		} else {
			logger.debug("Not requesting grafana, because the url or the api key is not configured.");
		}
	}

	private boolean isGrafanaConfigured(String grafanaUrl, String apiToken) {
		return StringUtils.isNotEmpty(grafanaUrl) && StringUtils.isNotEmpty(apiToken);
	}

	public void close() {
		asyncRestPool.shutdown();
	}

}

@@ -244,16 +244,64 @@
 cutor;%0A%0A
+import com.fasterxml.jackson.databind.JsonNode;%0A
 import c
@@ -343,24 +343,24 @@
 ObjectNode;%0A
-
 import org.s
@@ -828,16 +828,90 @@
 ient %7B%0A%0A
+%09private static final String ES_STAGEMONITOR_DS_NAME = %22ES stagemonitor%22;%0A
 %09private
@@ -1515,52 +1515,10 @@
 ing 
-name, final String url, int reportingInterva
+ur
 l) %7B
@@ -1609,20 +1609,39 @@
 %22name%22, 
-name
+ES_STAGEMONITOR_DS_NAME
 );%0A%09%09dat
@@ -2069,17 +2069,44 @@
 , %22%3E%22 + 
-r
+corePlugin.getElasticsearchR
 eporting
@@ -2113,16 +2113,18 @@
 Interval
+()
  + %22s%22);
@@ -2672,16 +2672,114 @@
 false);%0A
+%09%09%09addMinIntervalToPanels(dashboard, %22%3E%22 + corePlugin.getElasticsearchReportingInterval() + %22s%22);%0A
 %09%09%09final
@@ -2952,16 +2952,16 @@
 on e) %7B%0A
-
 %09%09%09logge
@@ -2987,32 +2987,378 @@
 (), e);%0A%09%09%7D%0A%09%7D%0A%0A
+%09private void addMinIntervalToPanels(ObjectNode dashboard, String interval) %7B%0A%09%09for (JsonNode row : dashboard.get(%22rows%22)) %7B%0A%09%09%09for (JsonNode panel : row.get(%22panels%22)) %7B%0A%09%09%09%09if (panel.has(%22datasource%22) && panel.get(%22datasource%22).asText().equals(ES_STAGEMONITOR_DS_NAME)) %7B%0A%09%09%09%09%09((ObjectNode) panel).put(%22interval%22, interval);%0A%09%09%09%09%7D%0A%09%09%09%7D%0A%09%09%7D%0A%09%7D%0A%0A
 %09private void as

Set min interval in Grafana dashboards