package eu.dnetlib.enabling.aas.ctx;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;

import eu.dnetlib.enabling.aas.admin.IAdministrationService;
import eu.dnetlib.enabling.aas.admin.ISecurityContextContainerAdministration;
import eu.dnetlib.enabling.aas.ctx.ISecurityContextContainer;
import eu.dnetlib.enabling.aas.ctx.SecurityContext;
import eu.dnetlib.enabling.aas.ctx.SecurityContextContainerException;
import eu.dnetlib.enabling.aas.rmi.Attribute;
import eu.dnetlib.enabling.aas.rmi.Obligation;
import eu.dnetlib.enabling.aas.rmi.TypedString;
import eu.dnetlib.enabling.aas.utils.SpringUtils;

/**
 * Make sure the IS is running.
 * @author mhorst
 *
 */
public class SecurityContextContainerTest {

	ApplicationContext ctx;
	ISecurityContextContainer secCtxContainer;
	
	@Before
	public void setUp() throws Exception {
		ctx = SpringUtils.getSpringContext(SpringUtils.DEFAULT_INTEGRATION_RESOURCE);
		secCtxContainer = (ISecurityContextContainer) ctx.getBean("SecurityContextContainer");
	}

	@After
	public void tearDown() throws Exception {
	}
	
	@Test
	public void testService() {
		assertNotNull(secCtxContainer);
	}

	@Test
	public void testCreateQueryAndDeleteContext() {
		String resourceId = "testResource";
		Map<String,Object> properties = new HashMap<String,Object>();
		Attribute[] attributes = new Attribute[1];
		Attribute attr = new Attribute();
		attr.setKey("testAttributeKey");
		attr.setValue("testAttributeValue");
		attr.setType("testAttributeType");
		attributes[0] = attr;
		TypedString[] identities = new TypedString[1];
		TypedString ident = new TypedString("testService","service");
		identities[0] = ident;
		Obligation[] obligations = new Obligation[2];
		Obligation oblig1 = new Obligation();
		oblig1.setResource("testResource");
		oblig1.setObligation("testOblig1");
		Obligation oblig2 = new Obligation();
		oblig2.setResource("testResource");
		oblig2.setObligation("testOblig2");
		obligations[0] = oblig1;
		obligations[1] = oblig2;
		properties.put(ISecurityContextContainer.ATTRIBUTES_PROPERTY_KEY, attributes);
		properties.put(ISecurityContextContainer.IDENTITIES_PROPERTY_KEY, identities);
		properties.put(ISecurityContextContainer.OBLIGATIONS_PROPERTY_KEY, obligations);
//		creating SecCtx
		SecurityContext newSecCtx = null;
		try {
			newSecCtx = secCtxContainer.createContext(resourceId, properties);
		} catch (SecurityContextContainerException e) {
			e.printStackTrace();
			fail("Exception occured!");
		}
		assertNotNull(newSecCtx);
		assertTrue(resourceId.equals(newSecCtx.getResourceId()));
		assertTrue(attributes.length==newSecCtx.getAttributes().length);
		assertTrue(identities.length==newSecCtx.getIdentities().length);
		assertTrue(obligations.length==newSecCtx.getObligations().length);
		assertNotNull(newSecCtx.getSecCtxId());
		assertNotNull(newSecCtx.getPrivKey());
		assertNotNull(newSecCtx.getPubKey());
		
		String[] secCtxIds = new String[1];
		secCtxIds[0] = newSecCtx.getSecCtxId();
//		finding SecCtx
		SecurityContext[] secCtxs = null;
		try {
			secCtxs = secCtxContainer.queryContexts(secCtxIds);
		} catch (SecurityContextContainerException e) {
			e.printStackTrace();
		}
		assertNotNull(secCtxs);
		SecurityContext foundNewSecCtx = findSecurityContext(newSecCtx.getSecCtxId(),secCtxs);
		assertNotNull(foundNewSecCtx);
		assertTrue(checkSecCtxsEqual(newSecCtx, foundNewSecCtx));
		
//		overwriting SecCtx
		properties.put(ISecurityContextContainer.ATTRIBUTES_PROPERTY_KEY, null);
		SecurityContext overwritenSecCtx = null;
		try {
			overwritenSecCtx = secCtxContainer.createContext(resourceId, properties);
		} catch (SecurityContextContainerException e) {
			e.printStackTrace();
		}
		assertNotNull(overwritenSecCtx);
		assertTrue(newSecCtx.getSecCtxId().equals(overwritenSecCtx.getSecCtxId()));
		assertTrue(newSecCtx.getResourceId().equals(overwritenSecCtx.getResourceId()));
		assertFalse(Arrays.equals(newSecCtx.getPrivKey(), overwritenSecCtx.getPrivKey()) && Arrays.equals(newSecCtx.getPubKey(), overwritenSecCtx.getPubKey()));
		assertNull(overwritenSecCtx.getAttributes());
		assertTrue(newSecCtx.getIdentities().length==overwritenSecCtx.getIdentities().length);
		assertTrue(newSecCtx.getObligations().length==overwritenSecCtx.getObligations().length);
		
//		printing out all secCtxs
		if (secCtxContainer instanceof ISecurityContextContainerAdministration) {
			ISecurityContextContainerAdministration secCtxContAdm = (ISecurityContextContainerAdministration) secCtxContainer;
			try {
				secCtxContAdm.dumpBufferedSecCtxs(System.out);
			} catch (SecurityContextContainerException e) {
				e.printStackTrace();
			}
		}
		
//		deleting SecCtx
		SecurityContext deletedSecCtx = null;
		try {
			deletedSecCtx = secCtxContainer.deleteContext(newSecCtx.getSecCtxId());
		} catch (SecurityContextContainerException e) {
			e.printStackTrace();
		}
		assertNotNull(deletedSecCtx);
		assertTrue(checkSecCtxsEqual(overwritenSecCtx, deletedSecCtx));
		
//		dropping all secCtxs!
		/*
		if (secCtxContainer instanceof ISecurityContextContainerAdministration) {
			ISecurityContextContainerAdministration secCtxContAdm = (ISecurityContextContainerAdministration) secCtxContainer;
			try {
				secCtxContAdm.dropAllSecCtxs();
			} catch (SecurityContextContainerException e) {
				e.printStackTrace();
			}
		}
		*/
		
		secCtxIds = new String[1];
		secCtxIds[0] = newSecCtx.getSecCtxId();
		secCtxs = null;
		try {
			secCtxs = secCtxContainer.queryContexts(secCtxIds);
		} catch (SecurityContextContainerException e) {
			e.printStackTrace();
		}
		assertNotNull(secCtxs);
		SecurityContext foundNullSecCtx = findSecurityContext(newSecCtx.getSecCtxId(),secCtxs);
		assertNull(foundNullSecCtx);
		
	}
	
	@Test
	public void testDeleteNonExistingSecCtx() {
		String secCtxId = "nonExistingSecCtxId";
		try {
			SecurityContext nonExistingSecCtx = secCtxContainer.deleteContext(secCtxId);
			assertNull(nonExistingSecCtx);
		} catch (SecurityContextContainerException e) {
			e.printStackTrace();
			fail("Exception occured");
		}
	}
	
	@Test
	public void testDeleteNonExistingInCacheSecCtx() {
		String resourceId = "testResource";
		Map<String,Object> properties = new HashMap<String,Object>();
		Attribute[] attributes = new Attribute[1];
		Attribute attr = new Attribute();
		attr.setKey("testAttributeKey");
		attr.setValue("testAttributeValue");
		attr.setType("testAttributeType");
		attributes[0] = attr;
		TypedString[] identities = new TypedString[1];
		TypedString ident = new TypedString("testService","service");
		identities[0] = ident;
		Obligation[] obligations = new Obligation[2];
		Obligation oblig1 = new Obligation();
		oblig1.setResource("testResource");
		oblig1.setObligation("testOblig1");
		Obligation oblig2 = new Obligation();
		oblig2.setResource("testResource");
		oblig2.setObligation("testOblig2");
		obligations[0] = oblig1;
		obligations[1] = oblig2;
		properties.put(ISecurityContextContainer.ATTRIBUTES_PROPERTY_KEY, attributes);
		properties.put(ISecurityContextContainer.IDENTITIES_PROPERTY_KEY, identities);
		properties.put(ISecurityContextContainer.OBLIGATIONS_PROPERTY_KEY, obligations);
//		creating SecCtx
		SecurityContext newSecCtx = null;
		try {
			newSecCtx = secCtxContainer.createContext(resourceId, properties);
		} catch (SecurityContextContainerException e) {
			e.printStackTrace();
			fail("Exception occured!");
		}
		assertNotNull(newSecCtx);
//		clearing cache, but leaving SecCtx in IS
		IAdministrationService adminService = (IAdministrationService) ctx.getBean("A2ServiceAdministration");
		adminService.invalidateSecCtxs();
//		removing secCtx;
		try {
			SecurityContext removedSecCtx = secCtxContainer.deleteContext(newSecCtx.getSecCtxId());
			assertNotNull(removedSecCtx);
			assertNotNull(removedSecCtx.getSecCtxId());
		} catch (SecurityContextContainerException e) {
			e.printStackTrace();
			fail("Exception occured");
		}
	}
	
	private SecurityContext findSecurityContext(String secCtxId, SecurityContext[] secCtxs) {
		if (secCtxId==null || secCtxs==null
				|| secCtxs.length==0)
			return null;
		for (int i = 0; i < secCtxs.length; i++) {
			if (secCtxs[i]!=null && secCtxId.equals(secCtxs[i].getSecCtxId()))
				return secCtxs[i];
		}
		return null;
	}
	
	/**
	 * Simple check. Checks secCtxId, resourceId and keys.
	 * @param secCtx1
	 * @param secCtx2
	 * @return
	 */
	private boolean checkSecCtxsEqual(SecurityContext secCtx1, SecurityContext secCtx2) {
		if (secCtx1==null && secCtx2==null)
			return true;
		if (secCtx1==null || secCtx2==null)
			return false;
		if (secCtx1.getSecCtxId().equals(secCtx2.getSecCtxId())) {
			if (secCtx1.getResourceId().equals(secCtx2.getResourceId())) {
				if (Arrays.equals(secCtx1.getPrivKey(), secCtx2.getPrivKey()) && Arrays.equals(secCtx1.getPubKey(), secCtx2.getPubKey()))
					return true;
			}
		}
		return false;
	}

}
