package eu.dnetlib.data.information.collectionservice;

import static org.junit.Assert.*;

import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;

import javax.annotation.Resource;

import org.apache.commons.io.IOUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import eu.dnetlib.data.information.collectionservice.rmi.CollectionService;
import eu.dnetlib.data.information.collectionservice.rmi.CollectionServiceException;
import eu.dnetlib.enabling.tools.ServiceLocator;
import eu.dnetlib.test.AbstractIntegrationContainerTest;

/**
 * Test for Collection Service.
 *
 * @author michele
 *
 */
@RunWith(SpringJUnit4ClassRunner.class)
public class CollectionServiceTest extends AbstractIntegrationContainerTest {
	/**
	 * Service under test.
	 */
	private CollectionService service;

	/**
	 * Collection service locator.
	 */
	@Resource(name = "collectionServiceLocator")
	private transient ServiceLocator<CollectionService> serviceLocator;

	/**
	 * sax reader.
	 */
	private final SAXReader reader = new SAXReader();

	/**
	 * collection under test id.
	 */
	private String collID;

	/**
	 * father of collection under test.
	 */
	private String fatherCollID;

	/**
	 * DOM of collection profile.
	 */
	private Document collProfileDom;

	/**
	 * new fake member condition.
	 */
	private static final String NEW_MCOND = "NEW_MEMB_COND";

	/**
	 * new fake subject.
	 */
	private static final String NEW_SUBJECT = "Computer Science";

	/**
	 * expected retrivial condition.
	 */
	private static final String EXPECTED_RCOND = "(NEW_MEMB_COND) OR (objIdentifier=doc01) OR (objIdentifier=doc02) OR (objIdentifier=doc03)";


	/**
	 * prepare for testing.
	 *
	 * @throws Exception
	 *             could happen
	 */
	@Before
	public void setUp() throws Exception {
		service = serviceLocator.getService();

		collID = registerSampleColl();
		fatherCollID = registerSampleColl();
		collProfileDom = reader.read(new StringReader(service.getCollection(collID)));
	}

	/**
	 * register a sample collection.
	 *
	 * @return id of collection
	 * @throws Exception
	 *             could happen
	 */
	private String registerSampleColl() throws Exception {
		final StringWriter writer = new StringWriter();
		IOUtils.copy(getClass().getResourceAsStream("collProfile.xml"), writer);
		final String profile = writer.getBuffer().toString();
		return service.createCollection(profile);
	}

	/**
	 * test create invalid collection.
	 *
	 * @throws CollectionServiceException
	 *             could happen
	 */
	@Test(expected = CollectionServiceException.class)
	public void testCreateCollectionInvalid() throws CollectionServiceException {
		service.createCollection("<Coll name='invalid' />");
	}

	/**
	 * create a valid test collection.
	 *
	 * @throws CollectionServiceException
	 *             could happen
	 * @throws IOException
	 *             could happen
	 */
	@Test
	public void testCreateCollectionValid() throws CollectionServiceException, IOException {
		assertTrue(collID.length() > 0);
	}

	/**
	 * get a collection.
	 *
	 * @throws CollectionServiceException
	 *             could happen
	 */
	@Test
	public void testGetCollection() throws CollectionServiceException {
		assertNotNull(collProfileDom);
	}

	/**
	 * Update membership condition.
	 *
	 * @throws CollectionServiceException
	 *             could happen
	 * @throws DocumentException
	 *             could happen
	 */
	@Test
	public void testUpdateCollectionMCond() throws CollectionServiceException, DocumentException {
		final String oldRCond = collProfileDom.valueOf("//RETRIEVAL_CONDITION");

		collProfileDom.selectSingleNode("//MEMBERSHIP_CONDITION").setText(NEW_MCOND);

		service.updateCollection(collProfileDom.asXML());

		final String collProfile = service.getCollection(collID);
		collProfileDom = reader.read(new StringReader(collProfile));

		final String newRCond = collProfileDom.valueOf("//RETRIEVAL_CONDITION");

		assertFalse(oldRCond.equals(newRCond));
		assertTrue(newRCond.equals(EXPECTED_RCOND));
	}

	/**
	 * Test father.
	 *
	 * @throws Exception
	 *             could happen
	 */
	@Test
	public void testUpdateCollectionFather() throws Exception {
		final String oldRCond = collProfileDom.valueOf("//RETRIEVAL_CONDITION");

		((Element) collProfileDom.selectSingleNode("//FATHER")).addAttribute("id", fatherCollID);

		service.updateCollection(collProfileDom.asXML());

		final String collProfile = service.getCollection(collID);
		collProfileDom = reader.read(new StringReader(collProfile));

		assertEquals(fatherCollID, collProfileDom.valueOf("//FATHER/@id"));

		final String newRCond = collProfileDom.valueOf("//RETRIEVAL_CONDITION");
		assertNotNull(newRCond);
		assertTrue(newRCond.contains(" AND "));
		assertTrue(newRCond.length() >= "A AND B".length());
		assertFalse(oldRCond.equals(newRCond));
	}

	/**
	 * test update other fields.
	 *
	 * @throws Exception
	 *             some exception
	 */
	@Test
	public void testUpdateCollectionOther() throws Exception {
		collProfileDom.selectSingleNode("//SUBJECT").setText(NEW_SUBJECT);
		String collProfile = collProfileDom.asXML();

		service.updateCollection(collProfile);

		collProfile = service.getCollection(collID);
		collProfileDom = reader.read(new StringReader(collProfile));

		assertTrue(collProfileDom.valueOf("//SUBJECT").equals(NEW_SUBJECT));
	}

	/**
	 * delete collection
	 *
	 * TODO: which exception!
	 *
	 * @throws CollectionServiceException could happen
	 */
	@Test(expected = Exception.class)
	public void testDeleteCollection() throws CollectionServiceException {
		service.deleteCollection(collID);
		service.getCollection(collID).length();
	}


	/**
	 * getCollections with null.
	 *
	 * @throws CollectionServiceException could happen
	 */
	@Test(expected = CollectionServiceException.class)
	public void testGetCollectionsForNullIds() throws CollectionServiceException {
		service.getCollections(null);
	}


}
