/*
 * Copyright 2013-2015 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
 *
 *      http://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.consul.discovery;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.cloud.client.DefaultServiceInstance;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.util.StringUtils;

import com.ecwid.consul.v1.ConsulClient;
import com.ecwid.consul.v1.QueryParams;
import com.ecwid.consul.v1.Response;
import com.ecwid.consul.v1.agent.model.Member;
import com.ecwid.consul.v1.agent.model.Self;
import com.ecwid.consul.v1.agent.model.Service;
import com.ecwid.consul.v1.health.model.HealthService;

import static org.springframework.cloud.consul.discovery.ConsulServerUtils.findHost;
import static org.springframework.cloud.consul.discovery.ConsulServerUtils.getMetadata;

import lombok.extern.apachecommons.CommonsLog;

/**
 * @author Spencer Gibb
 * @author Joe Athman
 */
@CommonsLog
public class ConsulDiscoveryClient implements DiscoveryClient {

	interface LocalResolver {
		String getInstanceId();
		Integer getPort();
	}

	private final ConsulClient client;
	private final ConsulDiscoveryProperties properties;
	private final LocalResolver localResolver;

	private ServerProperties serverProperties;

	@Deprecated
	public ConsulDiscoveryClient(ConsulClient client, final ConsulLifecycle lifecycle,
								 ConsulDiscoveryProperties properties) {
		this(client, properties, new LocalResolver() {
			@Override
			public String getInstanceId() {
				return lifecycle.getInstanceId();
			}

			@Override
			public Integer getPort() {
				return lifecycle.getConfiguredPort();
			}
		});
	}

	public ConsulDiscoveryClient(ConsulClient client, ConsulDiscoveryProperties properties,
				LocalResolver localResolver) {
		this.client = client;
		this.properties = properties;
		this.localResolver = localResolver;
	}

	public void setServerProperties(ServerProperties serverProperties) {
		this.serverProperties = serverProperties;
	}

	@Override
	public String description() {
		return "Spring Cloud Consul Discovery Client";
	}

	@Override
	public ServiceInstance getLocalServiceInstance() {
		Response<Map<String, Service>> agentServices = client.getAgentServices();
		Service service = agentServices.getValue().get(localResolver.getInstanceId());
		String instanceId;
		Integer port;
		Map<String, String> metadata;
		String host = "localhost";

		// if we have a response from consul, that is the ultimate source of truth
		if (service != null) {
			instanceId = service.getId();
			port = service.getPort();
			host = service.getAddress();
			metadata = getMetadata(service.getTags());
		} else {
			// possibly called before registration, use configuration or best guess
			log.warn("getLocalServiceInstance(): Unable to locate service in consul agent: "
					+ localResolver.getInstanceId());

			instanceId = localResolver.getInstanceId();
			port = localResolver.getPort();
			if (port != null && port == 0 && serverProperties != null
					&& serverProperties.getPort() != null) {
				port = serverProperties.getPort();
			}
			metadata = getMetadata(this.properties.getTags());

			if (StringUtils.hasText(this.properties.getHostname())) {
				host = this.properties.getHostname();
			} else if (this.properties.isPreferAgentAddress()){
				// try and use the agent host
				String agentHost = getAgentHost();
				if (agentHost != null) {
					host = agentHost;
				}
			}
		}

		if (port == null) {
			log.warn("getLocalServiceInstance(): Unable to determine port.");
			port = 0;
		}

		return new DefaultServiceInstance(instanceId, host, port, false, metadata);
	}

	private String getAgentHost() {
		Response<Self> agentSelf = client.getAgentSelf();
		Member member = agentSelf.getValue().getMember();
		if (member != null) {
			if (properties.isPreferIpAddress()) {
				return member.getAddress();
			} else if (StringUtils.hasText(member.getName())) {
				return member.getName();
			}
		}
		return null;
	}

	@Override
	public List<ServiceInstance> getInstances(final String serviceId) {
		return getInstances(serviceId, QueryParams.DEFAULT);
	}

	public List<ServiceInstance> getInstances(final String serviceId,
			final QueryParams queryParams) {
		List<ServiceInstance> instances = new ArrayList<>();

		addInstancesToList(instances, serviceId, queryParams);

		return instances;
	}

	private void addInstancesToList(List<ServiceInstance> instances, String serviceId,
			QueryParams queryParams) {

		String aclToken = properties.getAclToken();
		Response<List<HealthService>> services;
		if (StringUtils.hasText(aclToken)) {
			services = client.getHealthServices(serviceId,
					this.properties.getDefaultQueryTag(),
					this.properties.isQueryPassing(), queryParams, aclToken);
		}
		else {
			services = client.getHealthServices(serviceId,
					this.properties.getDefaultQueryTag(),
					this.properties.isQueryPassing(), queryParams);
		}
		for (HealthService service : services.getValue()) {
			String host = findHost(service);
			instances.add(new DefaultServiceInstance(serviceId, host, service
					.getService().getPort(), false, getMetadata(service)));
		}
	}

	public List<ServiceInstance> getAllInstances() {
		List<ServiceInstance> instances = new ArrayList<>();

		Response<Map<String, List<String>>> services = client
				.getCatalogServices(QueryParams.DEFAULT);
		for (String serviceId : services.getValue().keySet()) {
			addInstancesToList(instances, serviceId, QueryParams.DEFAULT);
		}
		return instances;
	}

	@Override
	public List<String> getServices() {
		String aclToken = properties.getAclToken();

		if (StringUtils.hasText(aclToken)) {
			return new ArrayList<>(client.getCatalogServices(QueryParams.DEFAULT, aclToken).getValue()
					.keySet());
		} else {
			return new ArrayList<>(client.getCatalogServices(QueryParams.DEFAULT).getValue()
					.keySet());
		}
	}
}

@@ -1712,16 +1712,23 @@
 ent %7B%0A%0A%09
+public 
 interfac

Make LocalResolver public.