package eu.dnetlib.functionality.cql.mongo;

import java.io.IOException;

import com.google.common.collect.Lists;
import com.mongodb.BasicDBObject;
import com.mongodb.MongoClient;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.bson.BsonDocument;
import org.bson.conversions.Bson;
import org.bson.types.ObjectId;
import org.junit.Ignore;
import org.junit.Test;
import org.z3950.zing.cql.CQLParseException;

import static com.mongodb.client.model.Filters.gt;
import static org.junit.Assert.assertEquals;

public class MongoCqlTranslatorTest {

	private static final Log log = LogFactory.getLog(MongoCqlTranslatorTest.class); // NOPMD by marko on 11/24/08 5:02 PM
	private final MongoCqlTranslator tr = new MongoCqlTranslator();

	@Test
	public void testParseExact() throws IOException, CQLParseException {
		Bson expected = new BasicDBObject("set", "CEDIASManuscripts");
		Bson o = tr.toMongo("set exact \"CEDIASManuscripts\"");
		assertEquals(expected, o);
	}

	@Test
	public void testParseEq() throws IOException, CQLParseException {
		Bson expected = new BasicDBObject("set", "CEDIASManuscripts");
		Bson o = tr.toMongo("set = \"CEDIASManuscripts\"");
		assertEquals(expected, o);
	}

	@Test
	public void testParseEquivalentExact() throws IOException, CQLParseException {
		Bson o = tr.toMongo("set = \"CEDIASManuscripts\"");
		Bson oExact = tr.toMongo("set exact \"CEDIASManuscripts\"");
		assertEquals(oExact, o);
	}

	@Test
	public void testParseNeq() throws IOException, CQLParseException {
		Bson expected = new BasicDBObject("set", new BasicDBObject("$ne", "CEDIASManuscripts"));
		Bson o = tr.toMongo("set <> \"CEDIASManuscripts\"");
		assertEquals(expected, o);
	}

	@Test
	public void testParseAnd() throws IOException, CQLParseException {
		BasicDBObject expected = new BasicDBObject("$and", Lists.newArrayList(new BasicDBObject("set", new BasicDBObject("$ne", "CEDIASManuscripts")),
				new BasicDBObject("pippo", new BasicDBObject("$gt", "x"))));
		Bson o = tr.toMongo("set <> \"CEDIASManuscripts\" AND pippo > x");
		log.info(o);
		assertEquals(expected, o);
	}

	@Test
	public void testParseOr() throws IOException, CQLParseException {
		BasicDBObject expected = new BasicDBObject("$or", Lists.newArrayList(new BasicDBObject("set", new BasicDBObject("$ne", "CEDIASManuscripts")),
				new BasicDBObject("pippo", new BasicDBObject("$gt", "x"))));
		Bson o = tr.toMongo("set <> \"CEDIASManuscripts\" OR pippo > x");
		log.info(o);
		assertEquals(expected, o);
	}

	@Test
	public void testParseNot() throws IOException, CQLParseException {
		BasicDBObject expected = new BasicDBObject("$and", Lists.newArrayList(new BasicDBObject("set", "CEDIASManuscripts"), new BasicDBObject("$not",
				new BasicDBObject("pippo", new BasicDBObject("$gt", "x")))));
		Bson o = tr.toMongo("set = \"CEDIASManuscripts\" NOT pippo > x");
		//log.info(o)
		assertEquals(expected, o);
	}

	@Test
	public void testParseStar() throws IOException, CQLParseException {
		BasicDBObject expected = new BasicDBObject();
		Bson o = tr.toMongo("*");
		Bson o2 = tr.toMongo("*=*");
		assertEquals(expected, o);
		assertEquals(expected, o2);
	}

	@Test
	public void testParseStarAnd() throws IOException, CQLParseException {
		BasicDBObject expected = new BasicDBObject("$and", Lists.newArrayList(new BasicDBObject(), new BasicDBObject("pippo", new BasicDBObject("$gt", "x"))));
		Bson o = tr.toMongo("* AND pippo > x");
		Bson o2 = tr.toMongo("*=* AND pippo > x");
		assertEquals(expected, o);
		assertEquals(expected, o2);
	}

	@Test
	public void testParseStarAnd2() throws IOException, CQLParseException {
		BasicDBObject expected = new BasicDBObject("$and", Lists.newArrayList(new BasicDBObject("resulttypeid", "publication"), new BasicDBObject("funder", new BasicDBObject("$exists", true))));
		Bson o = tr.toMongo("(resulttypeid exact \"publication\" and funder =*)");
		assertEquals(expected, o);
	}

	@Test
	public void testParseIdQuery() throws IOException, CQLParseException {
		BasicDBObject expected = new BasicDBObject("_id", new BasicDBObject("$gt", new ObjectId("5225e093aabf055637bf2c65")));
		Bson o = tr.toMongo("_id > 5225e093aabf055637bf2c65");
		assertEquals(expected, o);
	}

	@Test
	public void testLongQuery() throws IOException, CQLParseException {
		String q = "oaftype=\"result\" AND resulttypeid=\"publication\" AND (set=\"All_Ireland_Public_Health_Repository\" OR set=\"ARROW_DIT\" OR set=\"Cork_Open_Research_Archive\" OR set=\"DCU_Online_Research_Access_Service\" OR set=\"e-publications_RCSI\" OR set=\"Marine_Institute_Open_Access_Repository__OAR\" OR set=\"Maynooth_University_ePrints___eTheses_Archive\" OR set=\"Research_Repository_UCD\" OR set=\"STOR\" OR set=\"Trinity_s_Access_to_Research_Archive\" OR set=\"UCD_Digital_Library\" OR set=\"University_of_Limerick_Institutional_Repository\" OR set=\"Waterford_Institute_of_Technology_Repository\")";
		Bson o = tr.toMongo(q);
		log.info(o);
	}

	@Ignore
	@Test
	public void testParseWfLoggerQuery() throws IOException, CQLParseException {
		BasicDBObject expected = new BasicDBObject("$and",
				Lists.newArrayList(
						new BasicDBObject("parentDatasourceId", "opendoar____::2294"),
						new BasicDBObject("system:profileFamily", "aggregator"),
						new BasicDBObject("system:isCompletedSuccessfully", "true")));

		Bson o = tr.toMongo("{\"parentDatasourceId\" : \"opendoar____::2294\", \"system:profileFamily\" : \"aggregator\", \"system:isCompletedSuccessfully\" : \"true\" }");
		assertEquals(expected, o);
	}

	@Test
	public void testParseTimestamp() throws IOException, CQLParseException {
		Long l = new Long("1494945927504");
		BasicDBObject expected = new BasicDBObject("timestamp", new BasicDBObject("$gt",l));
		Bson filter =  gt("timestamp", l);
		assertEquals(expected.toBsonDocument(BsonDocument.class, MongoClient.getDefaultCodecRegistry()), filter.toBsonDocument(BsonDocument.class, MongoClient.getDefaultCodecRegistry()));
	}

	@Test
	public void testPM_PMCIDQueryNotinPubMedCentral() throws IOException, CQLParseException {
		String q = "((pidtype = \"pmc\" or pidtype = \"pmid\") and set <> \"PubMed_Central\" )";
		BasicDBObject expected = new BasicDBObject("$and", Lists.newArrayList(new BasicDBObject("$or", Lists.newArrayList(new BasicDBObject("pidtype", "pmc"), new BasicDBObject("pidtype", "pmid"))), new BasicDBObject("set", new BasicDBObject("$ne", "PubMed_Central"))));
		Bson o = tr.toMongo(q);
		assertEquals(expected, o);
	}

}
