package eu.dnetlib.openaire.directindex.objects;

import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.CALLS_REAL_METHODS;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.withSettings;

import java.io.InputStreamReader;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;

import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.velocity.app.VelocityEngine;
import org.dom4j.Document;
import org.dom4j.Node;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;

import com.google.common.collect.Lists;
import com.google.gson.Gson;

import eu.dnetlib.enabling.is.lookup.rmi.ISLookUpService;
import eu.dnetlib.openaire.directindex.api.DirecIndexApiException;
import eu.dnetlib.openaire.directindex.api.OpenAIRESubmitterUtils;

/**
 * Created by michele on 14/12/15.
 */
@RunWith(MockitoJUnitRunner.class)
public class ResultEntryToOafTest {

	private VelocityEngine ve;

	private final String community_api = "https://dev-openaire.d4science.org/openaire/community/";
	private final String dsm_api = "https://dev-openaire.d4science.org/openaire/ds/";

	@Mock
	private ISLookUpService lookUpService;

	private ResultEntryToOaf toOaf;
	private OpenAIRESubmitterUtils utils;

	@Before
	public void setUp() throws Exception {

		utils = mock(OpenAIRESubmitterUtils.class, withSettings().defaultAnswer(CALLS_REAL_METHODS));
		doReturn(Lists.newArrayList("dh-ch", "dariah", "bar", "x")).when(utils).translateZenodoCommunity("https://zenodo.org/communities/dimpo");
		doReturn(Lists.newArrayList()).when(utils).translateZenodoCommunity("https://zenodo.org/communities/dumbo");
		doReturn(Lists.newArrayList("noLabel", "enermaps")).when(utils).translateZenodoCommunity("https://sandbox.zenodo.org/communities/oac-ni");
		doReturn(Lists.newArrayList("noLabel", "enermaps")).when(utils).translateZenodoCommunity("https://zenodo.org/communities/oac-ni");
		doReturn(new DatasourceEntry("test________::XXX", "XXX", "xxxx________")).when(utils).findDatasource(anyString());
		doReturn(new DatasourceEntry("opendoar____::2659", "ZENODO", "od______2659")).when(utils).findDatasource("opendoar____::2659");

		toOaf = new ResultEntryToOaf(utils);

		final Properties props = new Properties();
		props.setProperty("resource.loader", "class");
		props.setProperty("class.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
		props.setProperty("runtime.log.logsystem.class", "org.apache.velocity.runtime.log.Log4JLogChute");
		ve = new VelocityEngine();
		ve.init(props);

		when(lookUpService.quickSearchProfile(anyString())).thenAnswer(invocation -> {
			final String query = invocation.getArguments()[0].toString();
			if (query.contains("dnet:result_typologies")) {
				return Arrays.asList("publication @@@ publication", "dataset @@@ dataset", "software @@@ software", "other @@@ other");
			} else if (query.contains("dnet:access_modes")) {
				return Arrays.asList("OPEN @@@ Open Access");
			} else if (query.contains("dnet:publication_resource")) {
				return Arrays.asList("0001 @@@ Journal Article");
			} else if (query.contains("dnet:pid_types")) {
				return Arrays.asList("oai @@@ Open Archive Initiative", "doi @@@ Digital object identifier");
			} else if (query.contains("dnet:languages")) {
				return Arrays.asList("ita @@@ Italian");
			} else if (query.contains("ContextDSResourceType")) {
				return Arrays
					.asList("egi::classification::natsc::math::stats @@@ Statistics and Probability", "egi::classification::natsc::math::pure @@@ Pure Mathematics", "egi::classification::natsc::math @@@ Mathematics", "egi::classification::natsc @@@ Natural Sciences", "egi::classification @@@ EGI classification scheme", "egi @@@ EGI", "enermaps @@@ Energy Research", "enermaps::selection @@@ Selected by the H2020 EnerMaps project");
			} else {
				return new ArrayList<String>();
			}
		});

	}

	@Test
	public void testAsIndexRecord() throws Exception {
		final ResultEntry pub = new ResultEntry();
		pub.setOriginalId("oai:zenodo.org:5109933");
		pub.setTitle("TEST TITLE <test>");
		pub.getAuthors().add("Michele Artini");
		pub.getAuthors().add("Claudio Atzori");
		pub.getAuthors().add("Alessia Bardi");
		pub.setPublisher("Test publisher");
		pub.setDescription("DESCRIPTION & DESCRIPTION & DESCRIPTION & DESCRIPTION & DESCRIPTION & DESCRIPTION & DESCRIPTION ");
		pub.setLanguage("ita");
		pub.setLicenseCode("OPEN");
		pub.setResourceType("0001");
		pub.setUrl("http://test.it/a=1&b=2");
		pub.getPids().add(new PidEntry("doi", "10.5281/zenodo.7373707"));
		pub.getPids().add(new PidEntry("doi", "10.5281/zenodo.7373706"));
		pub.getPids().add(new PidEntry("oai", "oai:1234"));
		pub.setCollectedFromId("opendoar____::2659");
		pub.setHostedById("opendoar____::2659");
		pub.getLinksToProjects().add("info:eu-repo/grantAgreement/EC/FP7/283595/EU//OpenAIREplus");
		pub.getLinksToProjects().add("info:eu-repo/grantAgreement/RCUK//244%2F909/EU/Making Capabilities Work/WorkAble");
		pub.getContexts().add("egi::classification::natsc::math::pure");
		pub.getContexts().add("egi::classification::natsc::math::stats");
		pub.getContexts().add("enermaps");
		pub.getContexts().add("enermaps::selection");
		final String xml = toOaf.asOafRecord(pub, ve, lookUpService, "http://oaf/oaf.xsd");

		final Document doc = new SAXReader().read(new StringReader(xml));

		final Node resultNode = doc.selectSingleNode("//oaf:result");
		Assert.assertNotNull(resultNode);
		final Node origIdNode = resultNode.selectSingleNode("./originalId");
		Assert.assertNotNull(origIdNode);
		Assert.assertEquals("oai:zenodo.org:5109933", origIdNode.getText());
		final Node titleNode = resultNode.selectSingleNode("./title");
		Assert.assertNotNull(titleNode);
		Assert.assertNotNull(titleNode.getText());
		final List<Node> accessRightsNodes = resultNode.selectNodes(".//accessright/@classid");
		for (final Node aNode : accessRightsNodes) {
			Assert.assertEquals("OPEN", aNode.getStringValue());
		}
		final Node bestARNode = resultNode.selectSingleNode("./bestaccessright/@classid");
		Assert.assertEquals("OPEN", bestARNode.getStringValue());

		Assert.assertEquals("doi_________::023bff24ddf2083caec38db12aa0fa01", doc.selectSingleNode("//*[local-name()='objIdentifier']").getStringValue());
		// final OutputFormat format = OutputFormat.createPrettyPrint();
		// final XMLWriter writer = new XMLWriter(System.out, format);
		// writer.write(doc);

	}

	@Test
	public void testAsIndexRecordAccessRight() throws Exception {
		final ResultEntry pub = new ResultEntry();
		pub.setOriginalId("oai:zenodo.org:5109933");
		pub.setTitle("TEST TITLE <test>");
		pub.getAuthors().add("Michele Artini");
		pub.getAuthors().add("Claudio Atzori");
		pub.getAuthors().add("Alessia Bardi");
		pub.setPublisher("Test publisher");
		pub.setDescription("DESCRIPTION & DESCRIPTION & DESCRIPTION & DESCRIPTION & DESCRIPTION & DESCRIPTION & DESCRIPTION ");
		pub.setLanguage("ita");
		pub.setLicenseCode("CLOSED");
		pub.setAccessRightCode("OPEN");
		pub.setResourceType("0001");
		pub.setUrl("http://test.it/a=1&b=2");
		pub.getPids().add(new PidEntry("doi", "10.000/xyz-123"));
		pub.getPids().add(new PidEntry("oai", "oai:1234"));
		pub.setCollectedFromId("opendoar____::2659");
		pub.setHostedById("opendoar____::2659");
		pub.getLinksToProjects().add("info:eu-repo/grantAgreement/EC/FP7/283595/EU//OpenAIREplus");
		pub.getLinksToProjects().add("info:eu-repo/grantAgreement/EC/FP7/244909/EU/Making Capabilities Work/WorkAble");
		pub.getContexts().add("egi::classification::natsc::math::pure");
		pub.getContexts().add("egi::classification::natsc::math::stats");
		final String xml = toOaf.asOafRecord(pub, ve, lookUpService, "http://oaf/oaf.xsd");

		final Document doc = new SAXReader().read(new StringReader(xml));

		final Node resultNode = doc.selectSingleNode("//oaf:result");
		Assert.assertNotNull(resultNode);
		final Node origIdNode = resultNode.selectSingleNode("./originalId");
		Assert.assertNotNull(origIdNode);
		Assert.assertEquals("oai:zenodo.org:5109933", origIdNode.getText());
		final Node titleNode = resultNode.selectSingleNode("./title");
		Assert.assertNotNull(titleNode);
		Assert.assertNotNull(titleNode.getText());
		final List<Node> accessRightsNodes = resultNode.selectNodes(".//accessright/@classid");
		for (final Node aNode : accessRightsNodes) {
			Assert.assertEquals("OPEN", aNode.getStringValue());
		}
		final Node bestARNode = resultNode.selectSingleNode("./bestaccessright/@classid");
		Assert.assertEquals("OPEN", bestARNode.getStringValue());

		Assert.assertEquals("doi_________::6aab886fa2b3aba5a10f0136035e7e8d", doc.selectSingleNode("//*[local-name()='objIdentifier']").getStringValue());

		// final OutputFormat format = OutputFormat.createPrettyPrint();
		// final XMLWriter writer = new XMLWriter(System.out, format);
		// writer.write(doc);

	}

	@Test
	public void testAsORP() throws Exception {
		final ResultEntry pub = new ResultEntry();
		pub.setOriginalId("ORIG_ID_1234");
		pub.setTitle("TEST TITLE <test>");
		pub.getAuthors().add("Michele Artini");
		pub.getAuthors().add("Claudio Atzori");
		pub.getAuthors().add("Alessia Bardi");
		pub.setPublisher("Test publisher");
		pub.setDescription("DESCRIPTION & DESCRIPTION & DESCRIPTION & DESCRIPTION & DESCRIPTION & DESCRIPTION & DESCRIPTION ");
		pub.setLanguage("ita");
		pub.setLicenseCode("CLOSED");
		pub.setAccessRightCode("OPEN");
		pub.setResourceType("0020");
		pub.setType("other");
		pub.setUrl("http://test.it/a=1&b=2");
		pub.getPids().add(new PidEntry("doi", "10.000/xyz-123"));
		pub.getPids().add(new PidEntry("oai", "oai:1234"));
		pub.setCollectedFromId("test________::zenodo");
		pub.setHostedById("test________::UNKNOWN");
		pub.getLinksToProjects().add("info:eu-repo/grantAgreement/EC/FP7/283595/EU//OpenAIREplus");
		pub.getLinksToProjects().add("info:eu-repo/grantAgreement/EC/FP7/244909/EU/Making Capabilities Work/WorkAble");
		pub.getContexts().add("egi::classification::natsc::math::pure");
		pub.getContexts().add("egi::classification::natsc::math::stats");
		final String xml = toOaf.asOafRecord(pub, ve, lookUpService, "http://oaf/oaf.xsd");

		final Document doc = new SAXReader().read(new StringReader(xml));

		final Node resultNode = doc.selectSingleNode("//oaf:result");
		Assert.assertNotNull(resultNode);
		Assert.assertEquals("other", resultNode.selectSingleNode("./resulttype/@classid").getStringValue());
		final Node origIdNode = resultNode.selectSingleNode("./originalId");
		Assert.assertNotNull(origIdNode);
		Assert.assertEquals("ORIG_ID_1234", origIdNode.getText());
		final Node titleNode = resultNode.selectSingleNode("./title");
		Assert.assertNotNull(titleNode);
		Assert.assertNotNull(titleNode.getText());
		final List<Node> accessRightsNodes = resultNode.selectNodes(".//accessright/@classid");
		for (final Node aNode : accessRightsNodes) {
			Assert.assertEquals("OPEN", aNode.getStringValue());
		}
		final Node bestARNode = resultNode.selectSingleNode("./bestaccessright/@classid");
		Assert.assertEquals("OPEN", bestARNode.getStringValue());

		// final OutputFormat format = OutputFormat.createPrettyPrint();
		// final XMLWriter writer = new XMLWriter(System.out, format);
		// writer.write(doc);

	}

	@Test
	public void testAsIndexRecord_json() throws Exception {
		testAsIndexRecord_json("test_record.json");

	}

	@Test
	public void testAsIndexRecord_json_with_greek_chars() throws Exception {

		testAsIndexRecord_json("test_record_with_greek_chars.json");

	}

	@Test
	public void testAsIndexRecord_openaireId() throws Exception {

		testAsIndexRecord_json("test_record_openaireId.json");

	}

	@Test(expected = DirecIndexApiException.class)
	public void testAsIndexRecord_wrongOpenaireId() throws Exception {

		testAsIndexRecord_json("test_record_wrong_openaireId.json");

	}

	@Test
	public void testAsIndexRecord_json_zenodo() throws Exception {

		testAsIndexRecord_json("test_zenodo.json");

	}

	@Test
	public void testAsIndexRecord_json_zenodoWithAmpersand() throws Exception {

		testAsIndexRecord_json("test_zenodoAmpersandEverywhere.json");

	}

	@Test
	public void testAsIndexRecord_json_software() throws Exception {

		testAsIndexRecord_json("test_record_software.json");

	}

	@Test
	public void testAsIndexRecord_json_orp() throws Exception {

		testAsIndexRecord_json("test_record_orp.json");

	}

	private void testAsIndexRecord_json(final String filename) throws Exception {
		final ResultEntry pub =
			new Gson().fromJson(new InputStreamReader(getClass().getResourceAsStream(filename)), ResultEntry.class);

		final String xml = toOaf.asOafRecord(pub, ve, lookUpService, "http://oaf/oaf.xsd");
		// System.out.println(xml);

		final Document doc = new SAXReader().read(new StringReader(xml));

		final OutputFormat format = OutputFormat.createPrettyPrint();

		final XMLWriter writer = new XMLWriter(System.out, format);

		writer.write(doc);

		/* writer.close(); */
	}

	@Test
	public void testAsIndexRecord_json_zenodocommunities() throws Exception {
		testAsIndexRecord_json("test_zenodo_community.json");
	}

	@Test
	public void testAsIndexRecordFromSandbox_json_zenodocommunities() throws Exception {
		testAsIndexRecord_json("test_zenodo_community2.json");
	}

	@Test
	public void testAsIndexRecordFromZenodo_intelcomp() throws Exception {
		testAsIndexRecord_json("test_zenodo_dmp_intelcomp.json");
	}

	@Test
	public void testEscapeUnicode() {
		final String unicodeTxt =
			"i.e. closed curves of the form $t\ud835\udfc4 [0,2\u03c0] \u21a6 (\\cos t)u + (\\sin t)v$ for suitable orthogonal vectors $u$";
		System.out.println(StringEscapeUtils.escapeXml11(unicodeTxt));
	}

	@Test
	public void testAsIndexRecordFromZenodo_doi() throws Exception {
		testAsIndexRecord_json("test_zenodo_doi.json");
	}

}
