package eu.dnetlib.data.mdstore;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.anyObject;
import static org.mockito.Matchers.contains;
import static org.mockito.Matchers.argThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.eq;

import java.util.HashMap;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentMatcher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import eu.dnetlib.enabling.is.lookup.rmi.ISLookUpException;
import eu.dnetlib.enabling.is.lookup.rmi.ISLookUpService;
import eu.dnetlib.enabling.is.registry.ISRegistryDocumentNotFoundException;
import eu.dnetlib.enabling.is.registry.rmi.ISRegistryException;
import eu.dnetlib.enabling.is.registry.rmi.ISRegistryService;
import eu.dnetlib.enabling.resultset.ResultSetFactory;
import eu.dnetlib.enabling.resultset.ResultSetListener;
import eu.dnetlib.enabling.tools.OpaqueResource;
import eu.dnetlib.enabling.tools.StaticServiceLocator;
import eu.dnetlib.enabling.tools.blackboard.BlackboardServerHandler;
import eu.dnetlib.enabling.tools.blackboard.BlackboardJob;
import eu.dnetlib.soap.cxf.JaxwsEndpointReferenceBuilder;

/**
 * mdstore mock test.
 *
 * @author marko
 *
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class MDStoreServiceMockImplTest {

	/**
	 * logger.
	 */
	private static final Log log = LogFactory.getLog(MDStoreServiceMockImplTest.class); // NOPMD by marko on 11/24/08 5:02 PM

	/**
	 * instance under test.
	 */
	@Autowired
	private transient MDStoreServiceMockImpl mdStoreService;

	/**
	 * notification handler.
	 */
	@Autowired
	private transient MDStoreMockNotificationHandler notificationHandler;

	/**
	 * blackboard handler mock.
	 */
	private transient BlackboardServerHandler blackboardHandler;

	/**
	 * resultset factory mock.
	 */
	private transient ResultSetFactory resultSetFactory;

	/**
	 * registry service mock.
	 */
	private transient ISRegistryService registryService;

	/**
	 * lookup service mock.
	 */
	private transient ISLookUpService lookupService;

	/**
	 * common setup.
	 */
	@Before
	public void setUp() {
		mdStoreService.setEprBuilder(mock(JaxwsEndpointReferenceBuilder.class));

		resultSetFactory = mock(ResultSetFactory.class);
		mdStoreService.setResultSetFactory(resultSetFactory);

		blackboardHandler = mock(BlackboardServerHandler.class);
		notificationHandler.setBlackboardHandler(blackboardHandler);

		registryService = mock(ISRegistryService.class);
		mdStoreService.setRegistryLocator(new StaticServiceLocator<ISRegistryService>(registryService));

		lookupService = mock(ISLookUpService.class);
		mdStoreService.setLookupLocator(new StaticServiceLocator<ISLookUpService>(lookupService));
	}

	/**
	 * test delivering of md records.
	 *
	 * @throws MDStoreServiceException
	 *             could happen
	 */
	@Test
	public void testDeliverMDRecords() throws MDStoreServiceException {
		mdStoreService.deliverMDRecords("123" + MockMDStoreDao.MDSTORE_ID_SUFFIX, "oai_dc", null, null, null);

		verify(resultSetFactory).createResultSet((ResultSetListener) anyObject());
	}

	/**
	 * test direct delivery of records.
	 *
	 * @throws MDStoreServiceException
	 *             could happen
	 */
	@Test
	public void testDeliverMDRecordDirect() throws MDStoreServiceException {
		final List<String> res = mdStoreService.deliverMDRecordsDirect_Range("123" + MockMDStoreDao.MDSTORE_ID_SUFFIX, 1, 2);
		assertNotNull("records", res);

		assertEquals("content", mdStoreService.deliverRecord("123" + MockMDStoreDao.MDSTORE_ID_SUFFIX, "1"), res.get(0));
	}

	/**
	 * Test delivery of one record.
	 *
	 * @throws MDStoreServiceException
	 *             could happen
	 */
	@Test
	public void testDeliverRecord() throws MDStoreServiceException {
		final String res = mdStoreService.deliverRecord("123" + MockMDStoreDao.MDSTORE_ID_SUFFIX, "1");
		assertNotNull("record", res);
		assertFalse("not empty", res.isEmpty());
		log.info(res);
	}

	/**
	 * test get list of mdstores.
	 */
	@Test
	public void testGetListOfMDStores() {
		assertNotNull(mdStoreService);

		List<String> mdstores = mdStoreService.getListOfMDStores();
		assertNotNull("mdstores", mdstores);

		log.info(mdstores);

		assertEquals("mdstores", 2, mdstores.size());
	}

	/**
	 * test records from resultset.
	 */
	@Test
	@Ignore
	public void testStoreMDRecordsFromRS() {
		fail("Not yet implemented");
	}

	/**
	 * test blackboard: delete.
	 *
	 * @throws ISRegistryException
	 *             mock
	 */
	@Test
	public void testBlackboardDelete() throws ISRegistryException {
		final String message = "<RESOURCE_PROFILE/>";

		BlackboardJob job = mock(BlackboardJob.class);
		when(blackboardHandler.getJob((OpaqueResource) anyObject())).thenReturn(job);
		when(job.getAction()).thenReturn("DELETE");

		final HashMap<String, String> parameters = new HashMap<String, String>();
		parameters.put("id", "123" + MockMDStoreDao.MDSTORE_ID_SUFFIX);
		when(job.getParameters()).thenReturn(parameters);

		mdStoreService.notify("123", "UPDATE.MDStoreServiceResourceType.123.RESOURCE_PROFILE.BODY.BLACKBOARD.LAST_REQUEST", "123", message);

		verify(registryService).deleteProfile("123" + MockMDStoreDao.MDSTORE_ID_SUFFIX);

		verify(blackboardHandler).ongoing(job);
		verify(blackboardHandler).done(job);
	}

	/**
	 * test blackboard: delete non existing mdstore.
	 *
	 * @throws ISRegistryException
	 *             mock
	 */
	@Test
	public void testBlackboardDeleteNonExisting() throws ISRegistryException {
		final String message = "<RESOURCE_PROFILE/>";

		BlackboardJob job = mock(BlackboardJob.class);
		when(blackboardHandler.getJob((OpaqueResource) anyObject())).thenReturn(job);
		when(job.getAction()).thenReturn("DELETE");

		final HashMap<String, String> parameters = new HashMap<String, String>();
		parameters.put("id", "123" + MockMDStoreDao.MDSTORE_ID_SUFFIX);
		when(job.getParameters()).thenReturn(parameters);

		final ISRegistryDocumentNotFoundException mockException = new ISRegistryDocumentNotFoundException("mock not found");
		when(registryService.deleteProfile("123" + MockMDStoreDao.MDSTORE_ID_SUFFIX)).thenThrow(mockException);

		mdStoreService.notify("123", "UPDATE.MDStoreServiceResourceType.123.RESOURCE_PROFILE.BODY.BLACKBOARD.LAST_REQUEST", "123", message);

		verify(blackboardHandler).ongoing(job);
		verify(blackboardHandler).failed(job, mockException);
		verify(blackboardHandler, times(0)).done(job);
	}

	/**
	 * test blackboard: create.
	 *
	 * @throws ISRegistryException
	 *             mock
	 * @throws MDStoreServiceException
	 *             could happen
	 * @throws ISLookUpException
	 *             could happen
	 */
	@Test
	public void testBlackboardCreate() throws ISRegistryException, MDStoreServiceException, ISLookUpException {
		final String message = "<RESOURCE_PROFILE/>";

		BlackboardJob job = mock(BlackboardJob.class);
		when(blackboardHandler.getJob((OpaqueResource) anyObject())).thenReturn(job);
		when(job.getAction()).thenReturn("CREATE");

		final HashMap<String, String> parameters = new HashMap<String, String>();
		parameters.put("format", "oai_dc");
		parameters.put("layout", "store");
		parameters.put("interpretation", "driver");
		when(job.getParameters()).thenReturn(parameters);

		when(registryService.registerProfile(contains("RESOURCE_PROFILE"))).thenReturn("123");

		mdStoreService.notify("123", "UPDATE.MDStoreServiceResourceType.123.RESOURCE_PROFILE.BODY.BLACKBOARD.LAST_REQUEST", "123", message);

		verify(blackboardHandler, times(0)).failed(eq(job), argThat(new ArgumentMatcher<Exception>() {

			@Override
			public boolean matches(final Object argument) {
				log.fatal("got exception", (Throwable) argument);
				return true;
			}
		}));

		final String mdId = job.getParameters().get("id");
		assertEquals("result id", "123", mdId);

		verify(blackboardHandler).ongoing(job);
		verify(blackboardHandler).done(job);

		verify(lookupService).quickSearchProfile(
				"for $x in collection('')//RESOURCE_PROFILE[.//RESOURCE_IDENTIFIER/@value/string() = '" + mdId
						+ "']//NUMBER_OF_RECORDS return update value $x with '2'");

		assertNotNull("content", mdStoreService.deliverRecord(mdId, "1"));

		// check that the newly created mdstore is really a wrapper for the first mock mdstore.
//		assertEquals("mock", mdStoreService.deliverRecord("123" + MockMDStoreDao.MDSTORE_ID_SUFFIX, "1"), mdStoreService.deliverRecord(mdId, "1"));
	}

}
