package eu.dnetlib.msro.workflows.nodes.transform;

import java.io.IOException;
import java.net.URLEncoder;
import java.util.List;
import javax.xml.ws.wsaddressing.W3CEndpointReference;

import com.googlecode.sarasvati.Arc;
import com.googlecode.sarasvati.NodeToken;
import eu.dnetlib.enabling.is.lookup.rmi.ISLookUpException;
import eu.dnetlib.enabling.is.lookup.rmi.ISLookUpService;
import eu.dnetlib.enabling.locators.UniqueServiceLocator;
import eu.dnetlib.enabling.resultset.XSLTMappedResultSetFactory;
import eu.dnetlib.enabling.resultset.client.utils.EPRUtils;
import eu.dnetlib.msro.rmi.MSROException;
import eu.dnetlib.msro.workflows.nodes.SimpleJobNode;
import org.antlr.stringtemplate.StringTemplate;
import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.core.io.Resource;

public class MdBuilderJobNode extends SimpleJobNode {

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

	private Resource mdBuilderTemplateXslt;

	private String inputEprParam;
	private String outputEprParam;
	private String datasourceId;
	private String datasourceInterface;

	private XSLTMappedResultSetFactory xsltMappedResultSetFactory;

	@javax.annotation.Resource
	private UniqueServiceLocator serviceLocator;

	@Override
	protected String execute(final NodeToken token) throws MSROException {
		final String inputEpr = token.getEnv().getAttribute(inputEprParam);

		if ((inputEpr == null) || inputEpr.isEmpty()) throw new MSROException("InputEprParam (" + inputEprParam + ") not found in ENV");
		StringTemplate st = null;
		try {
			st = new StringTemplate(IOUtils.toString(getMdBuilderTemplateXslt().getInputStream()));
			st.setAttribute("datasourceId", datasourceId);
			st.setAttribute("xpath", getMetadataIdentifierPath().replace("\"", "'"));
			st.setAttribute("baseurl", URLEncoder.encode(getBaseUrl(), "UTF-8"));
			st.setAttribute("metadatanamespace", getMetadataNamespace());

			/*
			 * If namespacePrefix has been already pushed to env by some custom JobNode e.g. ObtainOpenaireDataSourceParamsJobNode then push
			 * it to ST. Else: a) try to get it from EXTRAFIELDS of the datasource b) try to get it from DATASOURCE_ORIGINAL_ID of the
			 * datasource c) if any of the is present, then push to ST the datasourceId
			 */
			if (token.getEnv().hasAttribute("namespacePrefix")) {
				st.setAttribute("namespacePrefix", token.getEnv().getAttribute("namespacePrefix"));
			} else {
				List<String> namespacePrefix;
				String xQuery = "/*[.//RESOURCE_IDENTIFIER/@value='" + datasourceId + "']//EXTRA_FIELDS/FIELD/value[../key='NamespacePrefix']/string()";
				namespacePrefix = serviceLocator.getService(ISLookUpService.class).quickSearchProfile(xQuery);
				if (namespacePrefix.size() != 0) {
					st.setAttribute("namespacePrefix", namespacePrefix.get(0));
				} else {
					xQuery = "/*[.//RESOURCE_IDENTIFIER/@value='" + datasourceId + "']//DATASOURCE_ORIGINAL_ID/string()";
					namespacePrefix = serviceLocator.getService(ISLookUpService.class).quickSearchProfile(xQuery);
					if (namespacePrefix.size() != 0) {
						st.setAttribute("namespacePrefix", namespacePrefix.get(0));
					} else {
						st.setAttribute("namespacePrefix", datasourceId);
					}
				}
			}

			final W3CEndpointReference epr = xsltMappedResultSetFactory.createMappedResultSet(new EPRUtils().getEpr(inputEpr), st.toString());

			token.getEnv().setAttribute(outputEprParam, epr.toString());

			return Arc.DEFAULT_ARC;
		} catch (ISLookUpException e) {
			throw new MSROException("Error while initializing mdBuilder template (" + getMdBuilderTemplateXslt().getFilename() + ") for datasource "
					+ datasourceId, e);
		} catch (IOException e) {
			throw new MSROException("Error parsing template: " + getMdBuilderTemplateXslt().getFilename(), e);
		} catch (Exception e) {
			log.error(st.toString());
			throw new MSROException(e);
		}
	}

	private String getBaseUrl() throws ISLookUpException {
		String xQuery = "/*[.//RESOURCE_IDENTIFIER/@value='{datasourceId}']//INTERFACE[@id='{interfaceId}']//BASE_URL/string()";
		xQuery = xQuery.replace("{interfaceId}", datasourceInterface).replace("{datasourceId}", datasourceId);
		return serviceLocator.getService(ISLookUpService.class).getResourceProfileByQuery(xQuery);
	}

	private String getMetadataIdentifierPath() throws ISLookUpException {
		String xQuery = "for $x in collection('/db/DRIVER/RepositoryServiceResources/RepositoryServiceResourceType') "
				+ "return $x//INTERFACE[@id='{interfaceId}']/INTERFACE_EXTRA_FIELD[@name='metadata_identifier_path']/string()";
		xQuery = xQuery.replace("{interfaceId}", datasourceInterface);
		return serviceLocator.getService(ISLookUpService.class).getResourceProfileByQuery(xQuery);
	}

	private String getMetadataNamespace() {
		try {
			String xQuery = "let $x := /*[.//RESOURCE_IDENTIFIER/@value='{datasourceId}']//INTERFACE[@id='{interfaceId}']/ACCESS_PROTOCOL/@format/string() "
					+ "return /*[.//RESOURCE_TYPE/@value='MetadataFormatDSResourceType']//METADATAFORMAT[@Prefix=$x]/@NameSpace/string()";
			xQuery = xQuery.replace("{interfaceId}", datasourceInterface).replace("{datasourceId}", datasourceId);
			return serviceLocator.getService(ISLookUpService.class).getResourceProfileByQuery(xQuery);
		} catch (ISLookUpException e) {
			log.error("The interface is not OAI or the format is not found in the MetadataFormatDSResourceType, thus metadata format in the <about> section "
					+ "cannot managed here and it will be leaved empty (for the time being)");
			return "";
		}
	}

	public String getInputEprParam() {
		return inputEprParam;
	}

	public void setInputEprParam(final String inputEprParam) {
		this.inputEprParam = inputEprParam;
	}

	public String getOutputEprParam() {
		return outputEprParam;
	}

	public void setOutputEprParam(final String outputEprParam) {
		this.outputEprParam = outputEprParam;
	}

	public XSLTMappedResultSetFactory getXsltMappedResultSetFactory() {
		return xsltMappedResultSetFactory;
	}

	@Required
	public void setXsltMappedResultSetFactory(final XSLTMappedResultSetFactory xsltMappedResultSetFactory) {
		this.xsltMappedResultSetFactory = xsltMappedResultSetFactory;
	}

	public String getDatasourceId() {
		return datasourceId;
	}

	public void setDatasourceId(final String datasourceId) {
		this.datasourceId = datasourceId;
	}

	public String getDatasourceInterface() {
		return datasourceInterface;
	}

	public void setDatasourceInterface(final String datasourceInterface) {
		this.datasourceInterface = datasourceInterface;
	}

	public Resource getMdBuilderTemplateXslt() {
		return mdBuilderTemplateXslt;
	}

	@Required
	public void setMdBuilderTemplateXslt(final Resource mdBuilderTemplateXslt) {
		this.mdBuilderTemplateXslt = mdBuilderTemplateXslt;
	}

}
