package fr.ydelouis.selfoss.rest;

import android.net.Uri;
import android.util.Log;

import org.androidannotations.annotations.Bean;
import org.androidannotations.annotations.EBean;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpRequest;
import org.springframework.http.HttpStatus;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLDecoder;

import fr.ydelouis.selfoss.BuildConfig;
import fr.ydelouis.selfoss.account.SelfossAccount;
import fr.ydelouis.selfoss.util.Streams;

@EBean
public class SelfossApiInterceptor implements ClientHttpRequestInterceptor {

	private static final String TAG = "Selfoss API";
	private static final String KEY_USERNAME = "username";
	private static final String KEY_PASSWORD = "password";
	private static boolean LOG_REQUEST = BuildConfig.DEBUG && true;
	private static boolean LOG_FULL_REQUEST = BuildConfig.DEBUG && false;
	private static boolean LOG_RESPONSE = BuildConfig.DEBUG && false;

	@Bean protected SelfossAccount account;

	public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
		HttpRequest apiRequest = new ApiHttpRequest(request);
		logRequest(apiRequest);
		ApiHttpResponse response = new ApiHttpResponse(execution.execute(apiRequest, body));
		logResponse(response);
		return response;
	}

	private void logResponse(ApiHttpResponse response) throws IOException {
		if(LOG_RESPONSE)
			Log.i(TAG, response.getStatusCode().value() + " : " + Streams.stringOf(response.getBody()));
	}

	private void logRequest(HttpRequest request) throws UnsupportedEncodingException {
		String requestUri = URLDecoder.decode(request.getURI().toString(), "UTF-8");
		if(!LOG_FULL_REQUEST && LOG_REQUEST) {
			String url = account.getUrl();
			int start = requestUri.indexOf(url);
			if (start != -1) {
				requestUri = requestUri.substring(start+url.length());
			}
		}
		requestUri = hidePassword(requestUri);
		Log.i(TAG, request.getMethod() + " : " + requestUri);
	}

	private String hidePassword(String requestUri) {
		if (account.requireAuth()) {
			return requestUri.replace(account.getPassword(), "************");
		}
		return requestUri;
	}

	private class ApiHttpRequest implements HttpRequest
	{
		private HttpRequest httpRequest;

		public ApiHttpRequest(HttpRequest httpRequest) {
			this.httpRequest = httpRequest;
			this.httpRequest.getHeaders().set("Content-Length", "0");
			this.httpRequest.getHeaders().remove("Content-Type");
		}

		@Override
		public HttpHeaders getHeaders() {
			return httpRequest.getHeaders();
		}

		@Override
		public HttpMethod getMethod() {
			return httpRequest.getMethod();
		}

		@Override
		public URI getURI() {
			URI uri = httpRequest.getURI();
			Uri.Builder builder = new Uri.Builder();
			builder.scheme(getScheme());
			builder.authority(account.getUrl());
			builder.path(uri.getPath());
			builder.encodedQuery(uri.getQuery());
			if (account.requireAuth()) {
				builder.appendQueryParameter(KEY_USERNAME, account.getUsername());
				builder.appendQueryParameter(KEY_PASSWORD, account.getPassword());
			}
			Uri newUri = builder.build();
			String uriStr = newUri.toString().replace(newUri.getEncodedAuthority(), newUri.getAuthority());
			return URI.create(uriStr);
		}

		private String getScheme() {
			return account.useHttps() ? "https" : "http";
		}
	}

	private class ApiHttpResponse implements ClientHttpResponse
	{
		private ClientHttpResponse response;
		private byte[] byteResponse;

		public ApiHttpResponse(ClientHttpResponse response) {
			try {
				this.response = response;
				this.response.getHeaders().set("Content-Type", "application/json");
				byteResponse = Streams.byteArrayOf(response.getBody());
			} catch (IOException e) {
				e.printStackTrace();
			}
		}

		@Override
		public InputStream getBody() throws IOException {
			return new ByteArrayInputStream(byteResponse);
		}

		@Override
		public HttpHeaders getHeaders() {
			return response.getHeaders();
		}

		@Override
		public void close() {
			response.close();
		}

		@Override
		public int getRawStatusCode() throws IOException {
			return response.getRawStatusCode();
		}

		@Override
		public HttpStatus getStatusCode() throws IOException {
			return response.getStatusCode();
		}

		@Override
		public String getStatusText() throws IOException {
			return response.getStatusText();
		}
	}
}


@@ -52,16 +52,44 @@
 et.Uri;%0A
+import android.util.Base64;%0A
 import a
@@ -763,16 +763,49 @@
 Decoder;
+%0Aimport java.nio.charset.Charset;
 %0A%0Aimport
@@ -2906,16 +2906,365 @@
 -Type%22);
+%0A%0A            if (account.requireAuth()) %7B%0A                String auth = account.getUsername() + %22:%22 + account.getPassword();%0A                String authHeader = %22Basic %22 + Base64.encodeToString(auth.getBytes(Charset.forName(%22US-ASCII%22)), Base64.DEFAULT);%0A                this.httpRequest.getHeaders().set(%22Authorization%22, authHeader);%0A            %7D
 %0A%09%09%7D%0A%0A%09%09

send username password also as basic auth in header