package com.urbancode.util

import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;

import org.apache.http.Header;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

import groovy.json.JsonSlurper;

class TokenUtil {

	CloseableHttpClient client;
	private static final String TRUSTSTORE_DEFAULT_PASSWORD = "changeit";

	public CloseableHttpClient getClient() {
		return client;
	}

	public void setClient(CloseableHttpClient client) {
		this.client = client;
	}

	//Building HttpClient with default or custom SSLContext
	def CloseableHttpClient buildClient(def trustStorePath, def trustStorePassword)
	throws KeyManagementException, NoSuchAlgorithmException, KeyStoreException {
		if (trustStorePath) {
			if (trustStorePassword) {
				client = HttpClientBuilder.create().setSSLContext(trustStore(loadKeyStore(trustStorePath, trustStorePassword))).build();
				return client;
			} else {
				client = HttpClientBuilder.create().setSSLContext(trustStore(loadKeyStore(trustStorePath, TRUSTSTORE_DEFAULT_PASSWORD))).build();
				return client;
			}
		}
		client = HttpClientBuilder.create().build();
		return client;
	}

	//Initializing SSLContext and loading keystore
	def SSLContext trustStore(KeyStore store) throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
		TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
		tmf.init(store);
		SSLContext sslContext = SSLContext.getInstance("TLS"); //$NON-NLS-1$
		sslContext.init(null, tmf.getTrustManagers(), null);
		return sslContext;
	}

	//Initializing Keystore and loading certificates
	def KeyStore loadKeyStore(def trustStorePath, def password) throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException {
		KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
		keyStore.load(new FileInputStream(trustStorePath), password.toCharArray());
		return keyStore;
	}

	//Generates the access token
	def getToken(def offlineToken, def serverUrl, def trustStorePath, def trustStorePassword) {
		def parsedJson;
		buildClient(trustStorePath, trustStorePassword);
		HttpPost getTokenMethod = new HttpPost(serverUrl + '/rest/tokens/')
		getTokenMethod.addHeader("Accept", "application/json");
		getTokenMethod.addHeader("Content-Type", "application/x-www-form-urlencoded");
		ArrayList<NameValuePair> postParameters = new ArrayList<NameValuePair>();
		postParameters.add(new BasicNameValuePair("refresh_token", "${offlineToken}"));

		getTokenMethod.setEntity(new UrlEncodedFormEntity(postParameters, "UTF-8"));
		println "getProjectIdByName: '${getTokenMethod}'";
		def resp = client.execute(getTokenMethod);

		def statusCode = resp.getStatusLine().getStatusCode();
		if (statusCode < 200 || statusCode >= 300) {
			println "[Error] Token request failed with status ${statusCode}. Exiting Failure.";
			println('Response:\n' + resp.entity?.content?.getText("UTF-8"));
			System.exit(1);
		}
		if (statusCode == 403) {
			println "[Error] Error during retrieval of access token. Please check the license, request is unauthorized, Request returned response code ${statusCode}";
			System.exit(1);
		}
		def entity = EntityUtils.toString(resp.getEntity());

		def slurper = new JsonSlurper();
		try {
			parsedJson = slurper.parseText(entity);
		}
		catch (groovy.json.JsonException e) {
			println "Failed to parse response body. Printing useful debugging information and exiting.";
			println "Response status code: ${statusCode}.";
			println "Header:";
			Header[] headers = resp.getAllHeaders();
			for (Header header : headers) {
				System.out.println(header.getName() + ":" + header.getValue());
			}
			println "Body:";
			println entity;
			println "Stacktrace:";
			e.printStackTrace();
			System.exit(1);
		}
		return parsedJson
	}


}
