/**
 * Copyright 2017 Pivotal Software, Inc.
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.micrometer.spring.autoconfigure.export.prometheus;

import io.micrometer.core.annotation.Incubating;
import io.micrometer.core.instrument.Clock;
import io.micrometer.prometheus.PrometheusConfig;
import io.micrometer.prometheus.PrometheusMeterRegistry;
import io.micrometer.spring.autoconfigure.export.StringToDurationConverter;
import io.prometheus.client.CollectorRegistry;
import io.prometheus.client.exporter.PushGateway;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.actuate.autoconfigure.ManagementContextConfiguration;
import org.springframework.boot.actuate.endpoint.AbstractEndpoint;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.env.Environment;

import java.net.UnknownHostException;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

/**
 * Configuration for exporting metrics to Prometheus.
 *
 * @author Jon Schneider
 */
@Configuration
@ConditionalOnClass(PrometheusMeterRegistry.class)
@EnableConfigurationProperties(PrometheusProperties.class)
@Import(StringToDurationConverter.class)
public class PrometheusExportConfiguration {

    @Bean
    @ConditionalOnMissingBean
    public PrometheusConfig prometheusConfig(PrometheusProperties props) {
        return new PrometheusPropertiesConfigAdapter(props);
    }

    @Bean
    @ConditionalOnProperty(value = "management.metrics.export.prometheus.enabled", matchIfMissing = true)
    @ConditionalOnMissingBean
    public PrometheusMeterRegistry prometheusMeterRegistry(PrometheusConfig config, CollectorRegistry collectorRegistry,
                                                           Clock clock) {
        return new PrometheusMeterRegistry(config, collectorRegistry, clock);
    }

    @Bean
    @ConditionalOnMissingBean
    public CollectorRegistry collectorRegistry() {
        return new CollectorRegistry(true);
    }

    @ManagementContextConfiguration
    @ConditionalOnClass(AbstractEndpoint.class)
    public static class PrometheusScrapeEndpointConfiguration {
        @Bean
        public PrometheusScrapeEndpoint prometheusEndpoint(
                CollectorRegistry collectorRegistry) {
            return new PrometheusScrapeEndpoint(collectorRegistry);
        }

        @Bean
        public PrometheusScrapeMvcEndpoint prometheusMvcEndpoint(PrometheusScrapeEndpoint delegate) {
            return new PrometheusScrapeMvcEndpoint(delegate);
        }
    }

    /**
     * Configuration for <a href="https://github.com/prometheus/pushgateway">Prometheus Pushgateway</a>.
     *
     * @author David J. M. Karlsen
     */
    @Configuration
    @ConditionalOnClass(PushGateway.class)
    @ConditionalOnBean(PrometheusMeterRegistry.class)
    @ConditionalOnProperty("management.metrics.export.prometheus.pushgateway.enabled")
    @Incubating(since = "1.0.0")
    public class PrometheusPushGatewayConfiguration {
        private final Logger logger = LoggerFactory.getLogger(PrometheusPushGatewayConfiguration.class);
        private final CollectorRegistry collectorRegistry;
        private final PrometheusProperties.PushgatewayProperties pushgatewayProperties;
        private final PushGateway pushGateway;
        private final Environment environment;
        private final ScheduledFuture<?> pushFuture;

        PrometheusPushGatewayConfiguration(CollectorRegistry collectorRegistry, PrometheusProperties prometheusProperties,
                                           Environment environment) {
            this.collectorRegistry = collectorRegistry;
            this.pushgatewayProperties = prometheusProperties.getPushgateway();
            this.pushGateway = new PushGateway(pushgatewayProperties.getBaseUrl());
            this.environment = environment;
            this.pushFuture = Executors.newSingleThreadScheduledExecutor()
                    .scheduleAtFixedRate(this::push, pushgatewayProperties.getPushRate().toMillis(),
                            pushgatewayProperties.getPushRate().toMillis(), TimeUnit.MILLISECONDS);
        }

        void push() {
            try {
                pushGateway.pushAdd(collectorRegistry, job(), pushgatewayProperties.getGroupingKeys());
            } catch (UnknownHostException e) {
                logger.error("Unable to locate host '" + pushgatewayProperties.getBaseUrl() + "'. No longer attempting metrics publication to this host");
                pushFuture.cancel(false);
            } catch (Throwable t) {
                logger.error("Unable to push metrics to Prometheus Pushgateway", t);
            }
        }

        private String job() {
            String job = pushgatewayProperties.getJob();
            if (job == null) {
                job = environment.getProperty("spring.application.name");
            }
            if (job == null) {
                // There's a history of Prometheus spring integration defaulting the job name to "spring" from when
                // Prometheus integration didn't exist in Spring itself.
                job = "spring";
            }
            return job;
        }
    }
}

@@ -1302,32 +1302,41 @@
 igure.condition.
+AllNested
 ConditionalOnBea
@@ -1328,24 +1328,17 @@
 ondition
-alOnBean
+s
 ;%0Aimport
@@ -1698,24 +1698,83 @@
 ation.Bean;%0A
+import org.springframework.context.annotation.Conditional;%0A
 import org.s
@@ -3695,16 +3695,584 @@
  %7D%0A%0A
+    static class PrometheusPushGatewayEnabledCondition extends AllNestedConditions  %7B%0A        public PrometheusPushGatewayEnabledCondition() %7B%0A            super(ConfigurationPhase.PARSE_CONFIGURATION);%0A        %7D%0A%0A        @ConditionalOnProperty(value = %22management.metrics.export.prometheus.enabled%22, matchIfMissing = true)%0A        static class PrometheusMeterRegistryEnabled %7B%0A            //%0A        %7D%0A%0A        @ConditionalOnProperty(%22management.metrics.export.prometheus.pushgateway.enabled%22)%0A        static class PushGatewayEnabled %7B%0A            //%0A        %7D%0A    %7D%0A%0A
     /**%0A
     
@@ -4267,16 +4267,16 @@
     /**%0A
-
      * C
@@ -4504,14 +4504,8 @@
 onal
-OnBean
 (Pro
@@ -4515,114 +4515,41 @@
 heus
-MeterRegistry.class)%0A    @ConditionalOnProperty(%22management.metrics.export.prometheus.pushgateway.enabled%22
+PushGatewayEnabledCondition.class
 )%0A  

Fix conditional PushGateway configuration