package mesosphere.marathon.client;

import feign.*;
import feign.Feign.Builder;
import feign.auth.BasicAuthRequestInterceptor;
import feign.codec.ErrorDecoder;
import feign.gson.GsonDecoder;
import feign.gson.GsonEncoder;
import feign.slf4j.Slf4jLogger;
import mesosphere.client.common.ModelUtils;
import mesosphere.marathon.client.auth.TokenAuthRequestInterceptor;
import mesosphere.marathon.client.model.v2.ErrorResponse;

import java.io.IOException;

import static java.util.Arrays.asList;

public class MarathonClient {

	public static final String DEBUG_JSON_OUTPUT = "DEBUG_JSON_OUTPUT";

	static class MarathonHeadersInterceptor implements RequestInterceptor {
		@Override
		public void apply(RequestTemplate template) {
			template.header("Accept", "application/json");
			template.header("Content-Type", "application/json");
		}
	}

	static class MarathonErrorDecoder implements ErrorDecoder {
		@Override
		public Exception decode(String methodKey, Response response) {
			if (response.status() >= 400 && response.status() < 500 ) {
				try {
					ErrorResponse parsed = ModelUtils.GSON.fromJson(response.body().asReader(), ErrorResponse.class);
					return new MarathonException(response.status(), response.reason(), parsed);
				} catch (IOException e) {
					// intentionally nothing
				}
			}
			return new MarathonException(response.status(), response.reason());
		}
	}

	public static Marathon getInstance(String endpoint) {
		return getInstance(endpoint, null);
	}

	/**
	 * The generalized version of the method that allows more in-depth customizations via
	 * {@link RequestInterceptor}s.
	 *
	 * @param endpoint
	 * 		URL of Marathon
	 * @param interceptors optional request interceptors
	 * @return Marathon client
	 */
	public static Marathon getInstance(String endpoint, RequestInterceptor... interceptors) {

		Builder b = Feign.builder()
				.encoder(new GsonEncoder(ModelUtils.GSON))
				.decoder(new GsonDecoder(ModelUtils.GSON))
				.errorDecoder(new MarathonErrorDecoder());
		if (interceptors != null)
			b.requestInterceptors(asList(interceptors));
		String debugOutput = System.getenv(DEBUG_JSON_OUTPUT);
		if ("System.out".equals(debugOutput)) {
			System.setProperty("org.slf4j.simpleLogger.logFile", "System.out");
			System.setProperty("org.slf4j.simpleLogger.defaultLogLevel", "debug");
			b.logger(new Slf4jLogger()).logLevel(Logger.Level.FULL);
		} else if (debugOutput != null) {
			b.logger(new Logger.JavaLogger().appendToFile(debugOutput)).logLevel(Logger.Level.FULL);
		}
		b.requestInterceptor(new MarathonHeadersInterceptor());
		return b.target(Marathon.class, endpoint);
	}

	/**
	 * Creates a Marathon client proxy that performs HTTP basic authentication.
	 *
	 * @param endpoint URL for Marathon
	 * @param username basic auth username
	 * @param password basic auth password
	 * @return Marathon client
	 */
	public static Marathon getInstanceWithBasicAuth(String endpoint, String username, String password) {
		return getInstance(endpoint,new BasicAuthRequestInterceptor(username,password));
	}

	/**
	 * Creates a Marathon client proxy that uses a token for authentication.
	 *
	 * @param endpoint URL of Marathon
	 * @param token token
     * @return Marathon client
     */
	public static Marathon getInstanceWithTokenAuth(String endpoint, String token) {
		return getInstance(endpoint, new TokenAuthRequestInterceptor(token));
	}
}