package com.rjconsultores.ventaboletos.rest.generic;

import java.lang.reflect.Type;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;

import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import com.google.gson.Gson;
import com.rjconsultores.ventaboletos.enums.TipoEnvioRest;

public class GenericRest {

	private static final Logger log = LogManager.getLogger(GenericRest.class);

	private static GenericRest instance;

	public static synchronized GenericRest getInstance() {
		if (instance == null) {
			instance = new GenericRest();
		}
		return instance;
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	public <T> RetornoGenericRest fazerChamada(String uri, TipoEnvioRest metodo, String parametro,
			ContentType contentType, HashMap<String, String> headers, Class<T> targetClassType, Class<T> errorClass)
			throws Exception {

		HttpClient client = HttpClientBuilder.create().build();

		HttpUriRequest request = retornaRequestWithHeader(uri, metodo, parametro, contentType, headers);

		HttpResponse response = client.execute(request);

		RetornoGenericRest retorno = new RetornoGenericRest();
		retorno.setStatusResposta(response.getStatusLine().getStatusCode());
		retorno.setMensagemResposta(response.getStatusLine().getReasonPhrase());

		log.info(String.format("httpStatusCode: %s ", response.getStatusLine().getStatusCode()));

		if (response.getEntity() != null) {

			String entity = EntityUtils.toString(response.getEntity());

			if (retorno.getStatusResposta() == HttpStatus.SC_OK
					|| retorno.getStatusResposta() == HttpStatus.SC_CREATED) {
				if (targetClassType != null) {
					retorno.setConteudo(new Gson().fromJson(entity, targetClassType));
				}
			} else {
				retorno.setConteudoErro(new Gson().fromJson(entity, errorClass));
			}

			return retorno;
		}
		return null;
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	public <T> RetornoGenericRest fazerChamada(String uri, TipoEnvioRest metodo, String parametro,
			ContentType contentType, HashMap<String, String> headers, Type targetType, Class<T> errorClass)
			throws Exception {

		HttpClient client = HttpClientBuilder.create().build();

		HttpUriRequest request = retornaRequestWithHeader(uri, metodo, parametro, contentType, headers);

		HttpResponse response = client.execute(request);

		RetornoGenericRest retorno = new RetornoGenericRest();
		retorno.setStatusResposta(response.getStatusLine().getStatusCode());
		retorno.setMensagemResposta(response.getStatusLine().getReasonPhrase());

		log.info(String.format("httpStatusCode: %s ", response.getStatusLine().getStatusCode()));

		if (response.getEntity() != null) {
			String entity = EntityUtils.toString(response.getEntity());

			if (retorno.getStatusResposta() == HttpStatus.SC_OK
					|| retorno.getStatusResposta() == HttpStatus.SC_CREATED) {
				if (targetType != null) {
					retorno.setConteudo(new Gson().fromJson(entity, targetType));
				}
			} else {
				retorno.setConteudoErro(new Gson().fromJson(entity, errorClass));
			}

			return retorno;
		}
		return null;
	}

	private HttpUriRequest retornaRequestWithHeader(String uri, TipoEnvioRest metodo, String parametro,
			ContentType contentType, HashMap<String, String> headers) throws Exception {
		HttpUriRequest request = new HttpPost(uri);
		StringEntity body = new StringEntity(parametro != null ? parametro : "", "UTF-8");

		if (TipoEnvioRest.GET.equals(metodo)) {
			request = new HttpGet(parametro != null
					? montarUrlRequest(uri, URLEncoder.encode(parametro, Charset.forName("UTF-8").displayName()))
					: uri);
		} else if (TipoEnvioRest.POST.equals(metodo)) {
			request = new HttpPost(uri);
			((HttpPost) request).setEntity(body);
		} else if (TipoEnvioRest.DELETE.equals(metodo)) {
			request = new HttpDelete(montarUrlRequest(uri, parametro));
		} else if (TipoEnvioRest.PUT.equals(metodo)) {
			request = new HttpPut(montarUrlRequest(uri, parametro));
		} else {
			throw new Exception("No request type especified.");
		}

		for (Map.Entry<String, String> entry : headers.entrySet()) {
			String key = entry.getKey();
			String value = entry.getValue();
			request.addHeader(key, value);
		}
		
		if (headers != null && metodo != null) {
			headers.put("Content-type", contentType.getMimeType());
		}

		return request;
	}

	private String montarUrlRequest(String url, String path) {

		url = url.toLowerCase();

		if (!url.endsWith("/")) {
			url += "/";
		}

		return url.concat(path);
	}
}
