/*
 * Copyright 2013-2019 the original author or authors.
 *
 * 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
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * 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 org.springframework.cloud.gateway.discovery;

import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;

import org.junit.Test;
import org.junit.runner.RunWith;
import reactor.core.publisher.Flux;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.client.DefaultServiceInstance;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.ReactiveDiscoveryClient;
import org.springframework.cloud.client.discovery.event.HeartbeatEvent;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Bean;
import org.springframework.test.context.junit4.SpringRunner;

import static org.assertj.core.api.Assertions.assertThat;

@RunWith(SpringRunner.class)
@SpringBootTest(
		classes = DiscoveryClientRouteDefinitionLocatorIntegrationTests.Config.class,
		properties = { "spring.cloud.gateway.discovery.locator.enabled=true",
				"spring.cloud.gateway.discovery.locator.route-id-prefix=test__" })
public class DiscoveryClientRouteDefinitionLocatorIntegrationTests {

	@Autowired
	private RouteLocator routeLocator;

	@Autowired
	private ApplicationEventPublisher publisher;

	@Autowired
	private TestDiscoveryClient discoveryClient;

	@Test
	public void newServiceAddsRoute() throws Exception {
		List<Route> routes = routeLocator.getRoutes()
				.filter(route -> route.getId().startsWith("test__")).collectList()
				.block();
		assertThat(routes).hasSize(1);

		discoveryClient.multiple();

		publisher.publishEvent(new HeartbeatEvent(this, 1L));

		Thread.sleep(2000);

		routes = routeLocator.getRoutes()
				.filter(route -> route.getId().startsWith("test__")).collectList()
				.block();
		assertThat(routes).hasSize(2);
	}

	@SpringBootConfiguration
	@EnableAutoConfiguration
	protected static class Config {

		@Bean
		TestDiscoveryClient discoveryClient() {
			return new TestDiscoveryClient();
		}

	}

	private static class TestDiscoveryClient implements ReactiveDiscoveryClient {

		AtomicBoolean single = new AtomicBoolean(true);

		DefaultServiceInstance instance1 = new DefaultServiceInstance("service1_1",
				"service1", "localhost", 8001, false);

		DefaultServiceInstance instance2 = new DefaultServiceInstance("service2_1",
				"service2", "localhost", 8001, false);

		public void multiple() {
			single.set(false);
		}

		@Override
		public String description() {
			return null;
		}

		@Override
		public Flux<ServiceInstance> getInstances(String serviceId) {
			if (serviceId.equals("service1")) {
				return Flux.just(instance1);
			}
			if (serviceId.equals("service2")) {
				return Flux.just(instance2);
			}
			return Flux.empty();
		}

		@Override
		public Flux<String> getServices() {
			if (single.get()) {
				return Flux.just("service1");
			}
			return Flux.just("service1", "service2");
		}

	}

}