package eu.dnetlib.enabling.is.registry; // NOPMD

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.argThat;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

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

import javax.xml.bind.JAXBException;
import javax.xml.transform.dom.DOMSource;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;

import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentMatcher;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.w3c.dom.Element;
import org.w3c.dom.Node;

import eu.dnetlib.enabling.is.lookup.rmi.ISLookUpException;
import eu.dnetlib.enabling.is.lookup.rmi.ISLookUpService;
import eu.dnetlib.enabling.is.registry.rmi.ISRegistryException;
import eu.dnetlib.enabling.is.registry.rmi.ISRegistryService;
import eu.dnetlib.enabling.tools.OpaqueResource;
import eu.dnetlib.enabling.tools.StringOpaqueResource;
import eu.dnetlib.enabling.tools.blackboard.ActionStatus;
import eu.dnetlib.enabling.tools.blackboard.BlackboardMessage;
import eu.dnetlib.enabling.tools.blackboard.BlackboardMessageImpl;
import eu.dnetlib.enabling.tools.blackboard.BlackboardParameter;
import eu.dnetlib.enabling.tools.blackboard.BlackboardParameterImpl;
import eu.dnetlib.miscutils.jaxb.JaxbFactory;

/**
 * RegistryBlackboardManagerImpl test.
 * 
 * @author marko
 * 
 */
@RunWith(MockitoJUnitRunner.class)
public class RegistryBlackboardManagerImplTest {

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

	/**
	 * test message id.
	 */
	private static final String MSG_ID = "xxx";

	/**
	 * fake message date.
	 */
	private static final String MSG_DATE = "2009-03-19T17:55:45+01:00";

	/**
	 * service profile ID.
	 */
	private static final String PROF_ID = "123";

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

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

	/**
	 * instance under test.
	 */
	private transient RegistryBlackboardManagerImpl manager;

	/**
	 * common setup.
	 * 
	 * @throws JAXBException
	 *             could happen
	 * @throws IOException
	 *             shouldn't happen
	 * @throws ISLookUpException
	 *             mock
	 */
	@Before
	public void setUp() throws JAXBException, IOException, ISLookUpException {
		manager = new RegistryBlackboardManagerImpl();
		manager.setIsLookup(lookupService);
		manager.setRegistryService(registryService);

		manager.setMessageFactory(new JaxbFactory<BlackboardMessage>(BlackboardMessageImpl.class));

		manager.setMessageDater(new RegistryBlackboardManagerImpl.MessageDater() {

			@Override
			public String getCurrentDate() {
				return MSG_DATE;
			}

			@Override
			public String getNumericStamp() {
				return "1237483712.666000";
			}
		});
	}

	/**
	 * add message.
	 * 
	 * @throws JAXBException
	 *             could happen
	 * @throws ISRegistryException
	 *             could happen
	 * @throws IOException
	 *             could happen
	 * @throws ISLookUpException
	 *             mock
	 */
	@Test
	public void testAddMessage() throws JAXBException, ISRegistryException, IOException, ISLookUpException {
		final StringWriter serviceProfile = new StringWriter();
		IOUtils.copy(getClass().getResourceAsStream("serviceProfile.xml"), serviceProfile);
		when(lookupService.getResourceProfile(PROF_ID)).thenReturn(serviceProfile.toString());

		final BlackboardMessage message = manager.getMessageFactory().newInstance();
		message.setId(MSG_ID);
		message.setDate(MSG_DATE);
		message.setAction("CREATE");
		message.setActionStatus(ActionStatus.ASSIGNED);

		final BlackboardParameter param1 = new BlackboardParameterImpl();
		param1.setName("format");
		param1.setValue("DMF");
		message.getParameters().add(param1);

		final BlackboardParameter param2 = new BlackboardParameterImpl();
		param1.setName("id");
		param1.setValue("");
		message.getParameters().add(param2);

		manager.addMessage(PROF_ID, MSG_ID, manager.getMessageFactory().serialize(message));

		verify(registryService).updateProfile(eq(PROF_ID), argThat(new ArgumentMatcher<String>() {

			@Override
			public boolean matches(final String argument) {
				log.info("arg: " + argument);

				try {
					final OpaqueResource updatedResource = new StringOpaqueResource((String) argument);
					final Node messageNode = (Node) XPathFactory.newInstance().newXPath().evaluate("//BLACKBOARD/MESSAGE[last()]",
							updatedResource.asDom(), XPathConstants.NODE);

					final BlackboardMessage updatedMessage = manager.getMessageFactory().parse(new DOMSource(messageNode));

					assertEquals("messages should match", message, updatedMessage);

					final Element lastUpdated = (Element) XPathFactory.newInstance().newXPath().evaluate("//BLACKBOARD/LAST_REQUEST",
							updatedResource.asDom(), XPathConstants.NODE);
					assertEquals("stamp date", manager.getMessageDater().getNumericStamp(), lastUpdated.getAttribute("date"));
					assertEquals("stamp message", MSG_ID, lastUpdated.getTextContent());
				} catch (Exception e) {
					fail("got exception" + e);
				}
				return true;
			}
		}), anyString());
	}

	/**
	 * delete message.
	 * 
	 * @throws ISLookUpException
	 *             mock
	 * @throws IOException
	 *             could happen
	 * @throws ISRegistryException
	 *             mock
	 */
	@Test
	public void testDeleteMessage() throws ISLookUpException, IOException, ISRegistryException {
		final StringWriter serviceProfile = new StringWriter();

		IOUtils.copy(getClass().getResourceAsStream("serviceProfileWithMessage.xml"), serviceProfile);
		when(lookupService.getResourceProfile(PROF_ID)).thenReturn(serviceProfile.toString());

		manager.deleteMessage(PROF_ID, MSG_ID);

		verify(registryService).updateProfile(eq(PROF_ID), argThat(new ArgumentMatcher<String>() {

			@Override
			public boolean matches(final String argument) {
				log.debug("arg: " + argument);

				try {
					final OpaqueResource updatedResource = new StringOpaqueResource((String) argument);

					final Node messageNode = (Node) XPathFactory.newInstance().newXPath().evaluate("//BLACKBOARD/MESSAGE[last()]",
							updatedResource.asDom(), XPathConstants.NODE);

					assertNull("message should be deleted", messageNode);
				} catch (Exception e) {
					fail("got exception" + e);
				}
				return true;
			}
		}), anyString());

		// assertNotNull("dummy", manager);
	}

	/**
	 * replay message.
	 * 
	 * @throws ISLookUpException
	 *             mock
	 * @throws IOException
	 *             shouldn't happen
	 * @throws ISRegistryException
	 *             mock
	 * @throws JAXBException
	 *             could happen
	 */
	@Test
	public void testReplyMessage() throws ISLookUpException, IOException, ISRegistryException, JAXBException {
		final StringWriter serviceProfile = new StringWriter();

		IOUtils.copy(getClass().getResourceAsStream("serviceProfileWithMessage.xml"), serviceProfile);
		when(lookupService.getResourceProfile(PROF_ID)).thenReturn(serviceProfile.toString());

		final BlackboardMessage message = manager.getMessageFactory().newInstance();
		message.setId(MSG_ID);
		message.setDate(MSG_DATE);
		message.setAction("CREATE");
		message.setActionStatus(ActionStatus.ONGOING);

		final BlackboardParameter param1 = new BlackboardParameterImpl();
		param1.setName("format");
		param1.setValue("DMF");
		message.getParameters().add(param1);

		final BlackboardParameter param2 = new BlackboardParameterImpl();
		param1.setName("id");
		param1.setValue("");
		message.getParameters().add(param2);

		manager.replyMessage(PROF_ID, manager.getMessageFactory().serialize(message));

		verify(registryService).updateProfile(eq(PROF_ID), argThat(new ArgumentMatcher<String>() {

			@Override
			public boolean matches(final String argument) {
				log.info("arg: " + argument);

				try {
					final OpaqueResource updatedResource = new StringOpaqueResource((String) argument);

					final Node messageNode = (Node) XPathFactory.newInstance().newXPath().evaluate("//BLACKBOARD/MESSAGE[last()]",
							updatedResource.asDom(), XPathConstants.NODE);

					final BlackboardMessage updatedMessage = manager.getMessageFactory().parse(new DOMSource(messageNode));

					assertEquals("messages should match", message, updatedMessage);

					final Element lastUpdated = (Element) XPathFactory.newInstance().newXPath().evaluate("//BLACKBOARD/LAST_RESPONSE",
							updatedResource.asDom(), XPathConstants.NODE);
					assertEquals("stamp date", manager.getMessageDater().getNumericStamp(), lastUpdated.getAttribute("date"));
					assertEquals("stamp message", MSG_ID, lastUpdated.getTextContent());
				} catch (Exception e) {
					fail("got exception" + e);
				}
				return true;
			}
		}), anyString());
	}

}
