package eu.dnetlib.openaire.api.objects;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import eu.dnetlib.openaire.api.SinglePublicationSubmitterUtils;
import eu.dnetlib.rmi.enabling.ISLookUpException;
import eu.dnetlib.rmi.enabling.ISLookUpService;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.tools.generic.EscapeTool;
import org.springframework.ui.velocity.VelocityEngineUtils;

import eu.dnetlib.miscutils.datetime.DateUtils;
import eu.dnetlib.miscutils.functional.hash.Hashing;

/**
 * Created by michele on 02/12/15.
 */
public class PublicationEntry {

	private String originalId;
	private String title;
	private List<String> authors = new ArrayList<String>();
	private String publisher;
	private String description;
	private String language;
	private List<PidEntry> pids = new ArrayList<PidEntry>();
	private String licenseCode;
	private String embargoEndDate;

	public String getEmbargoEndDate() {
		return embargoEndDate;
	}

	public void setEmbargoEndDate(final String embargoEndDate) {
		this.embargoEndDate = embargoEndDate;
	}

	private String resourceType;
	private String url;
	private String collectedFromId;
	private String hostedById;

	// String according to openaire guidelines:
	// info:eu-repo/grantAgreement/Funder/FundingProgram/ProjectID/[Jurisdiction]/[ProjectName]/[ProjectAcronym]
	private List<String> contexts = new ArrayList<String>();

	// String according to the EGI context profile, example: egi::classification::natsc::math
	private List<String> linksToProjects = new ArrayList<String>();

	private static long last_cache_update = 0;
	private static final Map<String, Map<String, String>> cached_vocabularies = new HashMap<String, Map<String, String>>();
	private static final Map<String, DatasourceEntry> cached_datasources = new HashMap<String, DatasourceEntry>();
	private static final Map<String, String> cached_contexts = new HashMap<String, String>();

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

	public PublicationEntry() {}

	public String getOriginalId() {
		return originalId;
	}

	public void setOriginalId(final String originalId) {
		this.originalId = originalId;
	}

	public String getTitle() {
		return title;
	}

	public void setTitle(final String title) {
		this.title = title;
	}

	public List<String> getAuthors() {
		return authors;
	}

	public void setAuthors(final List<String> authors) {
		this.authors = authors;
	}

	public String getPublisher() {
		return publisher;
	}

	public void setPublisher(final String publisher) {
		this.publisher = publisher;
	}

	public String getDescription() {
		return description;
	}

	public void setDescription(final String description) {
		this.description = description;
	}

	public String getLanguage() {
		return language;
	}

	public void setLanguage(final String language) {
		this.language = language;
	}

	public List<PidEntry> getPids() {
		return pids;
	}

	public void setPids(final List<PidEntry> pids) {
		this.pids = pids;
	}

	public String getLicenseCode() {
		return licenseCode;
	}

	public void setLicenseCode(final String licenseCode) {
		this.licenseCode = licenseCode;
	}

	public String getResourceType() {
		return resourceType;
	}

	public void setResourceType(final String resourceType) {
		this.resourceType = resourceType;
	}

	public String getUrl() {
		return url;
	}

	public void setUrl(final String url) {
		this.url = url;
	}

	public String getCollectedFromId() {
		return collectedFromId;
	}

	public void setCollectedFromId(final String collectedFromId) {
		this.collectedFromId = collectedFromId;
	}

	public String getHostedById() {
		return hostedById;
	}

	public void setHostedById(final String hostedById) {
		this.hostedById = hostedById;
	}

	public List<String> getContexts() {
		return contexts;
	}

	public void setContexts(final List<String> contexts) {
		this.contexts = contexts;
	}

	public List<String> getLinksToProjects() {
		return linksToProjects;
	}

	public void setLinksToProjects(final List<String> linksToProjects) {
		this.linksToProjects = linksToProjects;
	}

	public String asOafRecord(final VelocityEngine ve,
			final ISLookUpService lookupService,
			final String oafSchemaLocation) throws Exception {

		final DatasourceEntry collectedFromEntry = getDatasourceInfo(collectedFromId, lookupService);
		final DatasourceEntry hostedByEntry = getDatasourceInfo(hostedById, lookupService);

		final String objId = collectedFromEntry.getPrefix() + "::" + Hashing.md5(originalId);

		final Map<String, Object> model = new HashMap<String, Object>();
		model.put("esc", new EscapeTool());
		model.put("util", new SinglePublicationSubmitterUtils(lookupService));
		model.put("pub", this);
		model.put("objIdentifier", objId);
		model.put("oafSchemaLocation", oafSchemaLocation);
		model.put("licenses", getVocabulary("dnet:access_modes", lookupService));
		model.put("resourceTypes", getVocabulary("dnet:publication_resource", lookupService));
		model.put("pidTypes", getVocabulary("dnet:pid_types", lookupService));
		model.put("languages", getVocabulary("dnet:languages", lookupService));
		model.put("contexts", getContexts(lookupService));
		model.put("dateOfCollection", (new SimpleDateFormat("yyyy-MM-dd\'T\'hh:mm:ss\'Z\'")).format(new Date()));
		model.put("collectedFrom", collectedFromEntry);
		model.put("hostedBy", hostedByEntry);

		return VelocityEngineUtils.mergeTemplateIntoString(ve, "/eu/dnetlib/msro/openaireplus/api/indexRecord.xml.vm", "UTF-8", model);
	}

	private synchronized static DatasourceEntry getDatasourceInfo(final String dsId, final ISLookUpService lookupService) throws ISLookUpException {
		if (StringUtils
				.isBlank(dsId)) { return new DatasourceEntry("openaire____::1256f046-bf1f-4afc-8b47-d0b147148b18", "Unknown Repository", "unknown_____"); }

		if (!cached_datasources.containsKey(dsId)) {
			final String query =
					"collection('/db/DRIVER/RepositoryServiceResources/RepositoryServiceResourceType')//CONFIGURATION[./DATASOURCE_ORIGINAL_ID='" + dsId
							+ "']/concat(./OFFICIAL_NAME, ' @@@ ', .//FIELD/value[../key='NamespacePrefix'])";
			final String s = lookupService.getResourceProfileByQuery(query);
			final String[] arr = s.split("@@@");

			final DatasourceEntry ds = new DatasourceEntry(dsId, arr[0].trim(), arr[1].trim());

			if (StringUtils.isBlank(ds.getName()) || StringUtils.isBlank(ds.getPrefix())) {
				log.error("Invalid datasource id: " + dsId);
				throw new ISLookUpException("Invalid datasource id: " + dsId);
			} else {
				cached_datasources.put(dsId, ds);
			}
		}

		return cached_datasources.get(dsId);

	}

	private synchronized static Map<String, String> getVocabulary(final String voc, final ISLookUpService lookupService) throws ISLookUpException {

		if (((DateUtils.now() - last_cache_update) < TimeUnit.MINUTES.toMillis(15)) && cached_vocabularies.containsKey(voc)) {
			return cached_vocabularies.get(voc);
		} else {
			final String query = "collection('/db/DRIVER/VocabularyDSResources/VocabularyDSResourceType')[.//VOCABULARY_NAME/@code='" + voc
					+ "']//TERM/concat(@code, ' @@@ ', @english_name)";

			final Map<String, String> map = new HashMap<String, String>();
			for (final String s : lookupService.quickSearchProfile(query)) {
				final String[] arr = s.split("@@@");
				map.put(arr[0].trim(), arr[1].trim());
			}

			cached_vocabularies.put(voc, map);

			last_cache_update = DateUtils.now();

			return map;
		}
	}

	private synchronized static Map<String, String> getContexts(final ISLookUpService lookupService) throws ISLookUpException {
		if (((DateUtils.now() - last_cache_update) > TimeUnit.MINUTES.toMillis(15)) || cached_contexts.isEmpty()) {
			final String query =
					"collection('/db/DRIVER/ContextDSResources/ContextDSResourceType')[.//context/@type='community']//*[name()='context' or name()='category' or name()='concept']/concat(@id, ' @@@ ', @label)";

			cached_contexts.clear();
			for (final String s : lookupService.quickSearchProfile(query)) {
				final String[] arr = s.split("@@@");
				cached_contexts.put(arr[0].trim(), arr[1].trim());
			}
			last_cache_update = DateUtils.now();
		}
		return cached_contexts;
	}
}
