package org.cloudfoundry.promregator.endpoint;

import java.time.Duration;
import java.util.ArrayList;
import java.util.List;

import org.cloudfoundry.promregator.rewrite.AbstractMetricFamilySamplesEnricher;
import org.cloudfoundry.promregator.rewrite.CFAllLabelsMetricFamilySamplesEnricher;
import org.cloudfoundry.promregator.rewrite.NullMetricFamilySamplesEnricher;
import org.cloudfoundry.promregator.scanner.Instance;
import org.cloudfoundry.promregator.scanner.ResolvedTarget;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.WebApplicationContext;

import io.prometheus.client.CollectorRegistry;
import io.prometheus.client.Gauge;
import io.prometheus.client.exporter.common.TextFormat;

@RestController
@Scope(value=WebApplicationContext.SCOPE_REQUEST) // see also https://github.com/promregator/promregator/issues/51
@RequestMapping(EndpointConstants.ENDPOINT_PATH_SINGLE_TARGET_SCRAPING+"/{applicationId}/{instanceNumber}")
public class SingleTargetMetricsEndpoint extends AbstractMetricsEndpoint {
	
	private static final Logger log = LoggerFactory.getLogger(SingleTargetMetricsEndpoint.class);

	private Instance instance;
	
	@GetMapping(produces=TextFormat.CONTENT_TYPE_004)
	public ResponseEntity<String> getMetrics(
			@PathVariable String applicationId, 
			@PathVariable String instanceNumber
			) {
		
		if (this.isLoopbackRequest()) {
			throw new LoopbackScrapingDetectedException("Erroneous Loopback Scraping request detected");
		}
		
		String instanceId = String.format("%s:%s", applicationId, instanceNumber);
		
		String response = null;
		try {
			response = this.handleRequest( discoveredApplicationId -> applicationId.equals(discoveredApplicationId)
			, requestInstance -> {
				if (requestInstance.getInstanceId().equals(instanceId)) {
					this.instance = requestInstance;
					return true;
				}
				
				return false;
			});
		} catch (ScrapingException e) {
			return new ResponseEntity<>(e.toString(), HttpStatus.NOT_FOUND);
		}
		
		return new ResponseEntity<>(response, HttpStatus.OK);
	}

	@Override
	protected boolean isIncludeGlobalMetrics() {
		// NB: This is done by PromregatorMetricsEndpoint in this scenario instead.
		return false;
	}

	@Override
	protected boolean isLabelEnrichmentSuppressable() {
		/*
		 * we only have metrics of a single Cloud Foundry application instance in our
		 * response. Thus, it is permitted that label enrichment may be suppressed (hence answering "true" here).
		 */
		return true;
	}
	
	@Override
	protected void handleScrapeDuration(CollectorRegistry requestRegistry, Duration duration) {
		/*
		 * Note: The scrape_duration_seconds metric is being passed on to Prometheus with
		 * the normal scraping request.
		 * If the configuration option promregator.scraping.labelEnrichment is disabled, then 
		 * the metric must also comply to this approach. Otherwise there might arise issues
		 * with rewriting in Prometheus.
		 */
		
		AbstractMetricFamilySamplesEnricher enricher = null;
		String[] ownTelemetryLabels = null;
		if (this.isLabelEnrichmentEnabled()) {
			if (this.instance == null) {
				log.warn("Internal inconsistency: Single Target Metrics Endpoint triggered, even though instance could not be detected; skipping scrape_duration");
				return;
			}
			
			ResolvedTarget t = this.instance.getTarget();
			ownTelemetryLabels = CFAllLabelsMetricFamilySamplesEnricher.getEnrichingLabelNames();
			enricher = new CFAllLabelsMetricFamilySamplesEnricher(t.getOrgName(), t.getSpaceName(), t.getApplicationName(), this.instance.getInstanceId());
		} else {
			ownTelemetryLabels = NullMetricFamilySamplesEnricher.getEnrichingLabelNames();
			enricher = new NullMetricFamilySamplesEnricher();
		}
		
		Gauge scrapeDuration = Gauge.build("promregator_scrape_duration_seconds", "Duration in seconds indicating how long scraping of all metrics took")
				.labelNames(ownTelemetryLabels)
				.register(requestRegistry);
		
		List<String> labelValues = enricher.getEnrichedLabelValues(new ArrayList<>(0));
		scrapeDuration.labels(labelValues.toArray(new String[0])).set(duration.toMillis() / 1000.0);
	}

	
}