package eu.dnetlib.functionality.userprofiling.app;

import static org.junit.Assert.assertEquals;
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.List;

import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
import org.junit.BeforeClass;
import org.junit.Test;

import eu.dnetlib.api.functionality.UserProfileService;
import eu.dnetlib.api.functionality.UserProfileServiceException;
import eu.dnetlib.clients.functionality.profile.ws.UserProfileWebService;
import eu.dnetlib.clients.functionality.profile.ws.UserProfileWebServiceClient;
import eu.dnetlib.domain.ServiceIdentity;
import eu.dnetlib.domain.functionality.UserProfile;
import eu.dnetlib.domain.functionality.UserProfileSearchCriteria;

public class TestUserProfileService {
	private final String email = "somebody2@somehost.eu";
	private final String password = "somepass";
	private final String firstname = "Joseph";
	private final String lastname = "Nevermind";
	private final String institution = "Well Known Institution of Doing Nothing";
	private final String activationId = "someActivationId";

	private static UserProfileService userProfileService = new UserProfileWebServiceClient();

	@BeforeClass
	public static void setup() throws Throwable {
		try {
			JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
			factory.setServiceClass(UserProfileWebService.class);
			factory.setAddress("http://localhost:8080/uoa-profile-latest/services/userProfileWebService");

			UserProfileWebService webService = (UserProfileWebService) factory.create();

			((UserProfileWebServiceClient) userProfileService).setWebService(webService);
		} catch (Throwable t) {
			t.printStackTrace();

			throw t;
		}
	}
	@Test
	public void testIdentify() {
		ServiceIdentity identify = userProfileService.identify();
		assertNotNull(identify);
		assertTrue(identify.toString().length() > 0);
		System.out.println("service version: "+identify);
	}
	
	@Test
	public void testCreateUserProfile() throws Exception {
		String userProfId = null;
		try {
			UserProfile userProfReq = new UserProfile();
			userProfReq.setEmail(email);
			userProfReq.setPassword(password);
			userProfReq.setFirstname(firstname);
			userProfReq.setLastname(lastname);
			userProfReq.setInstitution(institution);
			userProfReq.setActivationId(activationId);
			
			UserProfile userProfResp = userProfileService.saveUser(userProfReq);
			assertNotNull(userProfResp);
			assertNotNull(userProfResp.getResourceId());
			userProfId = userProfResp.getResourceId();
			
			UserProfile userProf = userProfileService.getUserById(userProfId);
			assertNotNull(userProf);
			assertEquals(email, userProf.getEmail());
			assertEquals(password, userProf.getPassword());
			assertEquals(firstname, userProf.getFirstname());
			assertEquals(lastname, userProf.getLastname());
			assertEquals(institution, userProf.getInstitution());
			assertEquals(activationId, userProf.getActivationId());
			assertEquals(userProfId, userProf.getResourceId());
//			checking default values, might be different
			assertNull(userProf.getActive());

		} finally {
			if (userProfId!=null)
				userProfileService.deleteUserById(userProfId);
		}
	}
	
	@Test
	public void testUpdateProfile() throws Exception {
		String userProfId = null;
		try {
			UserProfile userProfReq = new UserProfile();
			userProfReq.setEmail(email);
			userProfReq.setPassword(password);
			userProfReq.setFirstname(firstname);
			userProfReq.setLastname(lastname);
			userProfReq.setInstitution(institution);
			userProfReq.setActivationId(activationId);
			
			UserProfile userProfResp = userProfileService.saveUser(userProfReq);
			assertNotNull(userProfResp);
			assertNotNull(userProfResp.getResourceId());
			userProfId = userProfResp.getResourceId();
			
			UserProfile userProfUpdateReq = new UserProfile();
			userProfUpdateReq.setResourceId(userProfId);
			String newEmail = "new@somehost.eu";
			userProfUpdateReq.setEmail(newEmail);
			String newPass = "newPass";
			userProfUpdateReq.setPassword(newPass);
			String newFirstName = "newPass";
			userProfUpdateReq.setFirstname(newFirstName);
			String newLastName = "newPass";
			userProfUpdateReq.setLastname(newLastName);
			String newInstitution = "newPass";
			userProfUpdateReq.setInstitution(newInstitution);
			
			UserProfile userProfUpdateResp = userProfileService.saveUser(userProfUpdateReq);
			assertNotNull(userProfUpdateResp);
			assertEquals(userProfId, userProfUpdateResp.getResourceId());

			UserProfile updatedUserProf = userProfileService.getUserById(userProfId);
			assertEquals(newEmail, updatedUserProf.getEmail());
			assertEquals(newPass, updatedUserProf.getPassword());
			assertEquals(newFirstName, updatedUserProf.getFirstname());
			assertEquals(newLastName, updatedUserProf.getLastname());
			assertEquals(newInstitution, updatedUserProf.getInstitution());
			assertEquals(userProfId, updatedUserProf.getResourceId());

		} finally {
			if (userProfId!=null)
				userProfileService.deleteUserById(userProfId);
		}
	}
	
	@Test
	public void testUpdateProfileForInvalidProfId() throws Exception {
		try {
			String userProfileId = "nonExistingProfileId";
			UserProfile userProfile = new UserProfile();
			userProfile.setResourceId(userProfileId);
			userProfile.setEmail(email);
			userProfile.setPassword(password);
			userProfile.setFirstname(firstname);
			userProfile.setLastname(lastname);
			userProfile.setInstitution(institution);
			userProfile.setActivationId(activationId);
//			FIXME will it behave like in 'create new' mode but with userProfileId fixed? 
			userProfileService.saveUser(userProfile);
			fail("Exception should be thrown when updating non-existing user!");
		} catch (UserProfileServiceException e) {
//			ok
		}
	}
	
	@Test
	public void testCreateUserProfileNoEmail() throws Exception {
		String userProfId = null;
		try {
			UserProfile userProfReq = new UserProfile();
			userProfReq.setEmail(null);
			userProfReq.setPassword(password);
			userProfReq.setFirstname(firstname);
			userProfReq.setLastname(lastname);
			userProfReq.setInstitution(institution);
			userProfReq.setActivationId(activationId);
			UserProfile userProfResp = userProfileService.saveUser(userProfReq);
			if (userProfResp!=null) {
//				for removing profile in finally block if registered anyway
				userProfId = userProfResp.getResourceId();
			}
			fail("Exception should be thrown when registering user profile without email address!");
		} catch(UserProfileServiceException e) {
//			ok
		} finally {
			if (userProfId!=null)
				userProfileService.deleteUserById(userProfId);
		}
	}
	


	@Test
	public void testCreateUserProfileWithDuplicatedEmail() throws Exception {
		String userProfId1 = null;
		String userProfId2 = null;
		
		UserProfile userProfReq1 = new UserProfile();
		userProfReq1.setEmail(email);
		userProfReq1.setPassword(password);
		userProfReq1.setFirstname(firstname);
		userProfReq1.setLastname(lastname);
		userProfReq1.setInstitution(institution);
		userProfReq1.setActivationId(activationId);
		UserProfile userProfResp1 = userProfileService.saveUser(userProfReq1);
		assertNotNull(userProfResp1);
		assertNotNull(userProfResp1.getResourceId());
		userProfId1 = userProfResp1.getResourceId();
		
		try {
			UserProfile userProfReq2 = new UserProfile();
			userProfReq2.setEmail(email);
			userProfReq2.setPassword(password);
			userProfReq2.setFirstname(firstname);
			userProfReq2.setLastname(lastname);
			userProfReq2.setInstitution(institution);
			userProfReq2.setActivationId(activationId);
			UserProfile userProfResp2 = userProfileService.saveUser(userProfReq2);
			if (userProfResp2!=null) {
				userProfId2 = userProfResp2.getResourceId();
			}
			fail("Exception should be thrown when registering user profile with duplicated email address!");
		} catch(UserProfileServiceException e) {
//			ok
		} finally {
			if (userProfId1!=null)
				userProfileService.deleteUserById(userProfId1);
			if (userProfId2!=null)
				userProfileService.deleteUserById(userProfId2);
		}
	}
	
	@Test
	public void testGetUserProfile() throws Exception {
		String userProfId = null;
		try {
			UserProfile userProfReq = new UserProfile();
			userProfReq.setEmail(email);
			userProfReq.setPassword(password);
			userProfReq.setFirstname(firstname);
			userProfReq.setLastname(lastname);
			userProfReq.setInstitution(institution);
			userProfReq.setActivationId(activationId);
			
			UserProfile userProfResp = userProfileService.saveUser(userProfReq);
			assertNotNull(userProfResp);
			assertNotNull(userProfResp.getResourceId());
			userProfId = userProfResp.getResourceId();
			
			UserProfile userProf = userProfileService.getUserById(userProfId);
			assertNotNull(userProf);
			assertEquals(email, userProf.getEmail());
			assertEquals(password, userProf.getPassword());
			assertEquals(firstname, userProf.getFirstname());
			assertEquals(lastname, userProf.getLastname());
			assertEquals(institution, userProf.getInstitution());
			assertEquals(activationId, userProf.getActivationId());
			assertEquals(userProfId, userProf.getResourceId());
//			checking default values, might be different
			assertNull(userProf.getActive());

		} finally {
			if (userProfId!=null)
				userProfileService.deleteUserById(userProfId);
		}
	}
	
	@Test
	public void testGetUserProfileForNullId() {
		String userProfId = null;
		try {
			userProfileService.getUserById(userProfId);
			fail("Exception should be thrown when getting user profile for null id!");
		} catch(UserProfileServiceException e) {
//			ok
		}
	}

	@Test
	public void testGetUserProfileForNonExistingId() {
		String userProfId = "nonExistingUserProfId";
		try {
			userProfileService.getUserById(userProfId);
			fail("Exception should be thrown when getting user profile for null id!");
		} catch(UserProfileServiceException e) {
//			ok
		}
	}

	@Test
	public void testSearchUsers() throws Exception {
		String userProfId1 = null;
		String userProfId2 = null;
	
		try {
			UserProfile userProfReq1 = new UserProfile();
			userProfReq1.setEmail(email);
			userProfReq1.setPassword(password);
			userProfReq1.setFirstname(firstname);
			userProfReq1.setLastname(lastname);
			userProfReq1.setInstitution(institution);
			userProfReq1.setActivationId(activationId);
			UserProfile userProfResp1 = userProfileService.saveUser(userProfReq1);
			assertNotNull(userProfResp1);
			assertNotNull(userProfResp1.getResourceId());
			userProfId1 = userProfResp1.getResourceId();
			
			String email2 = "someone2@somehost.eu";
			UserProfile userProfReq2 = new UserProfile();
			userProfReq2.setEmail(email2);
			userProfReq2.setPassword(password);
			userProfReq2.setFirstname(firstname);
			userProfReq2.setLastname(lastname);
			userProfReq2.setInstitution(institution);
			userProfReq2.setActivationId(activationId);
			UserProfile userProfResp2 = userProfileService.saveUser(userProfReq2);
			assertNotNull(userProfResp2);
			assertNotNull(userProfResp2.getResourceId());
			userProfId2 = userProfResp2.getResourceId();
			
			UserProfileSearchCriteria criteria1 = new UserProfileSearchCriteria();
			criteria1.setEmail(email);
			List<UserProfile> results1 = userProfileService.searchUsers(criteria1);
			assertNotNull(results1);
			assertEquals(1, results1.size());
			assertNotNull(results1.get(0));
			assertEquals(userProfId1, results1.get(0).getResourceId());
			
			UserProfileSearchCriteria criteria2 = new UserProfileSearchCriteria();
			criteria2.setEmail(email2);
			List<UserProfile> results2 = userProfileService.searchUsers(criteria2);
			assertNotNull(results2);
			assertEquals(1, results2.size());
			assertNotNull(results2.get(0));
			assertEquals(userProfId2, results2.get(0).getResourceId());

			UserProfileSearchCriteria emptyCriteria = new UserProfileSearchCriteria();
//			should return all users, at least two registered
			List<UserProfile> allUsers = userProfileService.searchUsers(emptyCriteria);
			assertNotNull(allUsers);
			assertTrue(allUsers.size() >= 2);
		} finally {
			try {
				if (userProfId1!=null)
					userProfileService.deleteUserById(userProfId1);
			} catch (Exception e) {
				//to make sure all operations from finally block will be performed
				e.printStackTrace();
			}
			if (userProfId2!=null)
				userProfileService.deleteUserById(userProfId2);
		}
	}
	
	@Test
	public void testSearchUsersForCriteriaOfNonExistingUser() throws Exception {
		String userProfId1 = null;
	
		try {
			UserProfile userProfReq1 = new UserProfile();
			userProfReq1.setEmail(email);
			userProfReq1.setPassword(password);
			userProfReq1.setFirstname(firstname);
			userProfReq1.setLastname(lastname);
			userProfReq1.setInstitution(institution);
			userProfReq1.setActivationId(activationId);
			UserProfile userProfResp1 = userProfileService.saveUser(userProfReq1);
			assertNotNull(userProfResp1);
			assertNotNull(userProfResp1.getResourceId());
			userProfId1 = userProfResp1.getResourceId();
			
			UserProfileSearchCriteria criteria1 = new UserProfileSearchCriteria();
			criteria1.setEmail("nonExisting"+System.currentTimeMillis());
			List<UserProfile> results1 = userProfileService.searchUsers(criteria1);
			assertNotNull(results1);
			assertEquals(0, results1.size());
			
		} finally {
			if (userProfId1!=null)
				userProfileService.deleteUserById(userProfId1);
		}
	}
	
	@Test
	public void testSearchUserIds() throws Exception {
		String userProfId1 = null;
		String userProfId2 = null;
		
		try {
			UserProfile userProfReq1 = new UserProfile();
			userProfReq1.setEmail(email);
			userProfReq1.setPassword(password);
			userProfReq1.setFirstname(firstname);
			userProfReq1.setLastname(lastname);
			userProfReq1.setInstitution(institution);
			userProfReq1.setActivationId(activationId);
			UserProfile userProfResp1 = userProfileService.saveUser(userProfReq1);
			assertNotNull(userProfResp1);
			assertNotNull(userProfResp1.getResourceId());
			userProfId1 = userProfResp1.getResourceId();
			
			String email2 = "someone@somehost.eu";
			UserProfile userProfReq2 = new UserProfile();
			userProfReq2.setEmail(email2);
			userProfReq2.setPassword(password);
			userProfReq2.setFirstname(firstname);
			userProfReq2.setLastname(lastname);
			userProfReq2.setInstitution(institution);
			userProfReq2.setActivationId(activationId);
			UserProfile userProfResp2 = userProfileService.saveUser(userProfReq2);
			assertNotNull(userProfResp2);
			assertNotNull(userProfResp2.getResourceId());
			userProfId2 = userProfResp2.getResourceId();
			
			UserProfileSearchCriteria criteria1 = new UserProfileSearchCriteria();
			criteria1.setEmail(email);
			List<String> results1 = userProfileService.searchUserIds(criteria1);
			assertNotNull(results1);
			assertEquals(1, results1.size());
			assertNotNull(results1.get(0));
			assertEquals(userProfId1, results1.get(0));
			
			UserProfileSearchCriteria criteria2 = new UserProfileSearchCriteria();
			criteria2.setEmail(email2);
			List<String> results2 = userProfileService.searchUserIds(criteria2);
			assertNotNull(results2);
			assertEquals(1, results2.size());
			assertNotNull(results2.get(0));
			assertEquals(userProfId2, results2.get(0));

			UserProfileSearchCriteria emptyCriteria = new UserProfileSearchCriteria();
//			should return all users, at least two registered
			List<String> allUsers = userProfileService.searchUserIds(emptyCriteria);
			assertNotNull(allUsers);
			assertTrue(allUsers.size() >= 2);
		} finally {
			try {
				if (userProfId1!=null)
					userProfileService.deleteUserById(userProfId1);
			} catch (Exception e) {
				//to make sure all operations from finally block will be performed
				e.printStackTrace();
			}
			if (userProfId2!=null)
				userProfileService.deleteUserById(userProfId2);
		}
	}
	
	@Test
	public void testSearchUserIdsForDifferentCriteria() throws Exception {
		String userProfId1 = null;
		String userProfId2 = null;
		
		try {
			UserProfile userProfReq1 = new UserProfile();
			userProfReq1.setEmail(email);
			userProfReq1.setPassword(password);
			userProfReq1.setFirstname(firstname);
			userProfReq1.setLastname(lastname);
			userProfReq1.setInstitution(institution);
			userProfReq1.setActivationId(activationId);
			UserProfile userProfResp1 = userProfileService.saveUser(userProfReq1);
			assertNotNull(userProfResp1);
			assertNotNull(userProfResp1.getResourceId());
			userProfId1 = userProfResp1.getResourceId();
			
			String uniq = "" + System.currentTimeMillis();
			String email2 = "someone" + uniq + "@somehost.eu";
			String password2 = "password" + uniq;
			String firstname2 = "firstname" + uniq;
			String lastname2 = "lastname" + uniq;
			String institution2 = "institution" + uniq;
			String activationId2 = "activationId" + uniq;
			
			UserProfile userProfReq2 = new UserProfile();
			userProfReq2.setEmail(email2);
			userProfReq2.setPassword(password2);
			userProfReq2.setFirstname(firstname2);
			userProfReq2.setLastname(lastname2);
			userProfReq2.setInstitution(institution2);
			userProfReq2.setActivationId(activationId2);
			UserProfile userProfResp2 = userProfileService.saveUser(userProfReq2);
			assertNotNull(userProfResp2);
			assertNotNull(userProfResp2.getResourceId());
			userProfId2 = userProfResp2.getResourceId();
			
//			UserProfileSearchCriteria critPassword = new UserProfileSearchCriteria();
//			critPassword.setPassword(password2);
//			List<String> results1 = userProfileService.searchUserIds(critPassword);
//			assertNotNull(results1);
//			assertEquals(1, results1.size());
//			assertNotNull(results1.get(0));
//			assertEquals(userProfId2, results1.get(0));
//		
//			UserProfileSearchCriteria critCont = new UserProfileSearchCriteria();
//			critCont.setContains(email2);
//			results1 = userProfileService.searchUserIds(critCont);
//			assertNotNull(results1);
//			assertEquals(1, results1.size());
//			assertNotNull(results1.get(0));
//			assertEquals(userProfId2, results1.get(0));
//			
//			UserProfileSearchCriteria critActId = new UserProfileSearchCriteria();
//			critActId.setActivationId(activationId2);
//			results1 = userProfileService.searchUserIds(critActId);
//			assertNotNull(results1);
//			assertEquals(1, results1.size());
//			assertNotNull(results1.get(0));
//			assertEquals(userProfId2, results1.get(0));
//			
////			testing multiple conditions for single result
//			UserProfileSearchCriteria critMultiple = new UserProfileSearchCriteria();
//			critMultiple.setEmail(email2);
//			critMultiple.setPassword(password2);
//			critMultiple.setActivationId(activationId2);
//			results1 = userProfileService.searchUserIds(critMultiple);
//			assertNotNull(results1);
//			assertEquals(1, results1.size());
//			assertNotNull(results1.get(0));
//			assertEquals(userProfId2, results1.get(0));
//			
////			testing mutually excluded results
//			UserProfileSearchCriteria critMutExt = new UserProfileSearchCriteria();
//			critMutExt.setEmail(email2);
//			critMutExt.setPassword(password);
//			results1 = userProfileService.searchUserIds(critMutExt);
//			assertNotNull(results1);
//			assertEquals(0, results1.size());
			
		} finally {
			try {
				if (userProfId1!=null)
					userProfileService.deleteUserById(userProfId1);
			} catch (Exception e) {
				//to make sure all operations from finally block will be performed
				e.printStackTrace();
			}
			if (userProfId2!=null)
				userProfileService.deleteUserById(userProfId2);
		}
	}
	
	@Test
	public void testSearchUserIdsForCriteriaOfNonExistingUser() throws Exception {
		String userProfId1 = null;
	
		try {
			UserProfile userProfReq1 = new UserProfile();
			userProfReq1.setEmail(email);
			userProfReq1.setPassword(password);
			userProfReq1.setFirstname(firstname);
			userProfReq1.setLastname(lastname);
			userProfReq1.setInstitution(institution);
			userProfReq1.setActivationId(activationId);
			UserProfile userProfResp1 = userProfileService.saveUser(userProfReq1);
			assertNotNull(userProfResp1);
			assertNotNull(userProfResp1.getResourceId());
			userProfId1 = userProfResp1.getResourceId();
			
			UserProfileSearchCriteria criteria1 = new UserProfileSearchCriteria();
			criteria1.setEmail("nonExisting"+System.currentTimeMillis());
			List<String> results1 = userProfileService.searchUserIds(criteria1);
			assertNotNull(results1);
			assertEquals(0, results1.size());
			
		} finally {
			if (userProfId1!=null)
				userProfileService.deleteUserById(userProfId1);
		}
	}
	
	@Test
	public void testRemoveUserProfile() throws Exception {
		String userProfId = null;
		UserProfile userProfReq = new UserProfile();
		userProfReq.setEmail(email);
		userProfReq.setPassword(password);
		userProfReq.setFirstname(firstname);
		userProfReq.setLastname(lastname);
		userProfReq.setInstitution(institution);
		userProfReq.setActivationId(activationId);
		
		UserProfile userProfResp = userProfileService.saveUser(userProfReq);
		assertNotNull(userProfResp);
		assertNotNull(userProfResp.getResourceId());
		userProfId = userProfResp.getResourceId();
		
		userProfileService.deleteUserById(userProfId);
		try {
			userProfileService.getUserById(userProfId);
			fail("Exception should be thrown when getting non existing profile!");
		} catch (UserProfileServiceException e) {
//			ok
		}
	}

	@Test
	public void testRemoveUserProfileForNonExistingId() throws Exception {
		try {
			String userProfileId = "nonExistingProfileId"+System.currentTimeMillis();
			userProfileService.deleteUserById(userProfileId);
			fail("Exception should be thrown when removing non-existing user profile!");
		} catch (UserProfileServiceException e) {
//			ok
		}
	}


}