package eu.dnetlib.functionality.modular.ui.oai;

import com.google.common.base.Joiner;
import eu.dnetlib.functionality.modular.ui.oai.objects.OaiRequest;
import eu.dnetlib.functionality.modular.ui.oai.objects.ResponseDetails;
import eu.dnetlib.miscutils.datetime.DateUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.HttpClients;
import org.dom4j.Document;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import java.io.StringWriter;

@Controller
public class OaiExplorerInternalController {
	
	private final static Resource oaiXslt = new ClassPathResource("/eu/dnetlib/functionality/modular/ui/xslt/oai.xslt");
	
	private static final Log log = LogFactory.getLog(OaiExplorerInternalController.class);
	
	@RequestMapping("/ui/oai_verb")
	public @ResponseBody String oaiVerb(@RequestBody(required=true) OaiRequest req) throws Exception {
		return callOaiVerb(req);
	}
		
	@RequestMapping("/ui/test_oai_verb")
	public @ResponseBody ResponseDetails testOaiVerb(@RequestBody final OaiRequest req) throws Exception {
		final ResponseDetails response = new ResponseDetails();
		
		final Document doc = callOaiVerb(req, response);
		
		if (response.isValid() && doc != null) {
			final OaiRequest nextCall = new OaiRequest();
			nextCall.setBaseUrl(req.getBaseUrl());
			
			if ("Identify".equals(req.getVerb())) {
				nextCall.setVerb("ListSets");
			} else if ("ListSets".equals(req.getVerb())) {
				nextCall.setVerb("ListMetadataFormats");
			} else if ("ListMetadataFormats".equals(req.getVerb())) {
				nextCall.setVerb("ListRecords");
				if (doc.selectSingleNode("//*[local-name()='metadataPrefix' and text()='oai_dc']") != null) {
					nextCall.setMdf("oai_dc");
				} else {
					nextCall.setMdf(doc.selectSingleNode("//*[local-name()='metadataPrefix']").getText());
				}
			} else if ("ListRecords".equals(req.getVerb())) {
				nextCall.setVerb("ListIdentifiers");
				nextCall.setMdf(req.getMdf());
			} else if ("ListIdentifiers".equals(req.getVerb())) {
				nextCall.setVerb("GetRecord");
				nextCall.setMdf(req.getMdf());
				nextCall.setId(doc.selectSingleNode("//*[local-name()='identifier']").getText());
			} else if ("GetRecord".equals(req.getVerb())) {
				// NOTHING
			}
			response.setNextCall(nextCall);
		}
		return response;
	}
	
	@RequestMapping("/ui/test_harvesting")
	public @ResponseBody ResponseDetails testHarvesting(@RequestBody final OaiRequest req) throws Exception {
		final ResponseDetails response = new ResponseDetails();
		
		final Document doc = callOaiVerb(req, response);
		
		if (response.isValid() && doc != null) {
			
			final Node node = doc.selectSingleNode("//*[local-name() = 'resumptionToken']");
			
			if (node != null) {
				response.setSize(doc.selectNodes("//*[local-name()='" + req.getVerb() + "']/*[local-name() != 'resumptionToken']").size());
				response.setCursor(NumberUtils.toInt(node.valueOf("@cursor"), -1));
				response.setTotal(NumberUtils.toInt(node.valueOf("@completeListSize"), -1));
				
				final OaiRequest nextCall = new OaiRequest();
				nextCall.setBaseUrl(req.getBaseUrl());
				nextCall.setVerb(req.getVerb());
				nextCall.setToken(node.getText());
				response.setNextCall(nextCall);
			}
		}
		return response;
	}
	
	private String callOaiVerb(final OaiRequest req) throws Exception {

		final HttpGet method = new HttpGet(!req.toQueryParams().isEmpty() ? req.getBaseUrl() + "?" + Joiner.on("&").join(req.toQueryParams()) : req.getBaseUrl());

		method.addHeader("Content-type", "text/xml; charset=UTF-8");

		try(CloseableHttpResponse response = HttpClients.createDefault().execute(method)) {

			int responseCode = response.getStatusLine().getStatusCode();
			if (HttpStatus.SC_OK != responseCode) {
				log.error("Error downloading from baseUrl: " + req.getBaseUrl());
				throw new RuntimeException("Error: " + responseCode);
			}

			final TransformerFactory tfactory = TransformerFactory.newInstance();

			final Transformer transformer = tfactory.newTransformer(new StreamSource(oaiXslt.getInputStream()));
			final StringWriter output = new StringWriter();
			transformer.transform(new StreamSource(response.getEntity().getContent()), new StreamResult(output));

			return output.toString();
		}
	}
	
	private Document callOaiVerb(final OaiRequest req, final ResponseDetails details) {

		final HttpGet method = new HttpGet(!req.toQueryParams().isEmpty() ? req.getBaseUrl() + "?" + Joiner.on("&").join(req.toQueryParams()) : req.getBaseUrl());
		method.addHeader("Content-type", "text/xml; charset=UTF-8");
		
		Document doc = null;

		final long start = DateUtils.now();

		try(CloseableHttpResponse response = HttpClients.createDefault().execute(method)) {

			int responseCode = response.getStatusLine().getStatusCode();
	
			details.setHttpCode(responseCode);
			details.setValid(HttpStatus.SC_OK == responseCode);
			
			if (HttpStatus.SC_OK == responseCode) {
				try {
					doc = new SAXReader().read(response.getEntity().getContent());
				} catch (Exception e) {
					details.setValid(false);
					details.setError(e.getMessage());
				}
			}
		} catch (Exception e) {
			details.setValid(false);
			details.setError(e.getMessage());
		}
				
		details.setTime(DateUtils.now() - start);
		details.setVerb(req.getVerb());
		
		return doc;
	}
	
}
