package eu.dnetlib.ariadneplus.catalogue;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;

import com.google.common.collect.Lists;
import eu.dnetlib.ariadneplus.jrr.AriadnePlusRegistryResource;
import eu.dnetlib.ariadneplus.publisher.AriadnePlusPublisherException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.client.utils.URIBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.*;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

/**
 * Created by Alessia Bardi on 08/03/2018.
 *
 * @author Alessia Bardi
 */
@Component
public class CatalogueAPIClient {

	private static final Log log = LogFactory.getLog(CatalogueAPIClient.class);

	@Value("${gcube.catalogue.baseurl}")
	private String baseURL;
	private final String itemPath = "items/";
	@Value("${gcube.registry.application.token}")
	private String applicationToken;
	@Value("${gcube.uri.resolver}")
	private String uriResolver;
	private String resolverBodyTemplate = "{ \"entity_name\": \"%s\" }";
	private String purgeBodyTemplate = "{\"id\":\"%s\"}";

	private HttpHeaders headersForResolver;
	private HttpHeaders headersForCatalogue;

	@Autowired
	private RestTemplate restTemplate;

	public CatalogueAPIClient(){
		headersForResolver = new HttpHeaders();
		headersForResolver.setContentType(MediaType.APPLICATION_JSON);

		headersForCatalogue = new HttpHeaders();
		headersForCatalogue.setContentType(MediaType.APPLICATION_JSON);
		headersForCatalogue.setAccept(Lists.newArrayList(MediaType.APPLICATION_JSON));
		headersForCatalogue.set("gcube-token", getApplicationToken());
	}


	/**
	 * Calls the ariadneplus resolver to get the name to use for the catalogue
	 * @param resName Given a AriadnePlus URL like: http://ariadneplus.d4science.org/handle/Ariadne/AriadnePortal/rhdsq5xm3e40, the resName is Ariadne/AriadnePortal/rhdsq5xm3e40.
	 * @return the name as computed by the AriadnePlus resolver. Null if the request was not successfull.
	 */
	public String getNameForCatalogue(final String resName) throws URISyntaxException {
		log.debug("Calling AriadnePlus resolver for "+resName);
		String body = String.format(resolverBodyTemplate, resName);
		HttpEntity<String> entity = new HttpEntity<String>(body, headersForResolver);
		URI uri = new URIBuilder(getUriResolver()).build();
		log.debug("Resolver at "+getUriResolver()+" post body:\n"+body);
		ResponseEntity<String> res = restTemplate.exchange(uri, HttpMethod.POST, entity, String.class);
		if(res.getStatusCode().is2xxSuccessful()){
			String resolved = res.getBody();
			log.debug(String.format("AriadnePlus resolver resolved %s with %s", resName, resolved ));
			return resolved;
		}
		else{
			log.debug(String.format("AriadnePlus resolver returned %s with cause %s for %s", res.getStatusCodeValue(), res.getStatusCode().getReasonPhrase(), resName));
			return null;
		}
	}

	protected boolean isRegistered(final String resCatName) throws AriadnePlusPublisherException {
		log.debug(String.format("Catalogue --> Checking if item %s exists", resCatName));
		HttpEntity<String> entity = new HttpEntity<String>(headersForCatalogue);
		try {
			URI uri = new URIBuilder(getBaseURL()+itemPath).build();
			ResponseEntity<String> response
					= restTemplate.exchange(uri + "/"+resCatName, HttpMethod.GET, entity, String.class);
			return response.getStatusCode().is2xxSuccessful();
		} catch (Throwable t) {
			throw new AriadnePlusPublisherException(t);
		}
	}

	@Deprecated
	public AriadnePlusRegistryResource getRegistered(final String resCatName) throws AriadnePlusPublisherException {
		log.debug(String.format("Catalogue --> Checking if item %s exists", resCatName));
		HttpEntity<String> entity = new HttpEntity<String>(headersForCatalogue);
		try {
			URI uri = new URIBuilder(getBaseURL()+itemPath).build();
			ResponseEntity<String> response
					= restTemplate.exchange(uri + "/"+resCatName, HttpMethod.GET, entity, String.class);
			if(response.getStatusCode().is2xxSuccessful()){
				CatalogueAPIResponse body = new CatalogueAPIResponse();
				body.setResponseBody(response.getBody());
				AriadnePlusRegistryResource r = body.getAriadnePlusRegistryResource();
				log.debug(String.format("Resource %s is in the catalogue with uuid %s", resCatName, r.getUuid()));
				return r;
			}
			else{
				log.debug(String.format("Resource %s is not in the catalogue yet", resCatName));
				return null;
			}
		} catch (Throwable t) {
			throw new AriadnePlusPublisherException(t);
		}
	}

	public boolean doRegister(final String json, final String resCatName) throws URISyntaxException, IOException {
		log.debug(String.format("Catalogue --> Registering item %s : %s", resCatName, json));
		return doCatalogueCall(json, resCatName, HttpMethod.POST);
	}

	public boolean doUpdate(final String json, final String resCatName) throws IOException, URISyntaxException {
		log.debug(String.format("Catalogue --> Updating item %s : %s", resCatName, json));
		return doCatalogueCall(json, resCatName, HttpMethod.PUT);
	}

	protected boolean doCatalogueCall(final String json, final String resCatName, final HttpMethod method) throws URISyntaxException, IOException {
		HttpEntity<String> entity = new HttpEntity<String>(json, headersForCatalogue);
		URI uri = new URIBuilder(getBaseURL()+itemPath).build();
		ResponseEntity<String> res = restTemplate.exchange(uri, method, entity, String.class);
		if (res.getStatusCode().is2xxSuccessful()) {
			return true;
		} else {
			log.error(String.format("Method %s on resource %s failed with code %s, reason: %s", method.name(), resCatName, res.getStatusCodeValue(), res.getBody()));
			return false;
		}
	}

	protected boolean purgeItem(final String resCatName) throws URISyntaxException, AriadnePlusPublisherException {
		log.debug(String.format("Catalogue --> Purge Item %s", resCatName));
		HttpEntity<String> entity = new HttpEntity<String>(headersForCatalogue);
		try {
			URI uri = new URIBuilder(getBaseURL()+itemPath).build();
			ResponseEntity<String> res
					= restTemplate.exchange(uri + "/"+resCatName+"?purge=true", HttpMethod.DELETE, entity, String.class);
			if (res.getStatusCode().is2xxSuccessful()) {
				return true;
			} else {
				log.error(String.format("Cannot purge item %s. HTTP error code %s, reason: %s", resCatName, res.getStatusCodeValue(), res.getBody()));
				return false;
			}
		} catch (Throwable t) {
			throw new AriadnePlusPublisherException(t);
		}
	}


	public String getBaseURL() {
		return baseURL;
	}

	public void setBaseURL(final String baseURL) {
		this.baseURL = baseURL;
	}

	public String getApplicationToken() {
		return applicationToken;
	}

	public void setApplicationToken(final String applicationToken) {
		this.applicationToken = applicationToken;
	}

	public RestTemplate getRestTemplate() {
		return restTemplate;
	}

	public void setRestTemplate(final RestTemplate restTemplate) {
		this.restTemplate = restTemplate;
	}

	public String getUriResolver() {
		return uriResolver;
	}

	public void setUriResolver(final String uri_resolver) {
		this.uriResolver = uriResolver;
	}

	public String getPurgeBodyTemplate() {
		return purgeBodyTemplate;
	}

	public void setPurgeBodyTemplate(final String purgeBodyTemplate) {
		this.purgeBodyTemplate = purgeBodyTemplate;
	}

}
