package eu.dnetlib.functionality.userprofiling.app;

import eu.dnetlib.api.functionality.CollectionService;
import eu.dnetlib.api.functionality.CollectionServiceException;
import eu.dnetlib.api.functionality.UserProfileService;
import eu.dnetlib.api.functionality.UserProfileServiceException;
import eu.dnetlib.domain.functionality.Collection;
import eu.dnetlib.domain.functionality.CollectionSearchCriteria;
import eu.dnetlib.domain.functionality.UserProfile;
import eu.dnetlib.domain.functionality.UserProfileSearchCriteria;
import eu.dnetlib.functionality.userprofiling.dao.UserProfileDAO;
import gr.uoa.di.driver.app.DriverServiceImpl;
import gr.uoa.di.driver.dao.DAOException;
import gr.uoa.di.driver.util.ServiceLocator;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;

import org.apache.log4j.Logger;

public class UserProfileServiceImpl extends DriverServiceImpl implements
		UserProfileService {

	private static Logger logger = Logger
			.getLogger(UserProfileServiceImpl.class);
	private UserProfileDAO dao = null;

	private String adminEmails = null;
	private ServiceLocator<CollectionService> collectionServiceLocator = null;

	public void init() {
		super.init();

		ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
		
		executor.schedule(new Runnable() {
			public void run() {
				checkUserCollections();
			}
			
		}, 30, TimeUnit.SECONDS);
		
		executor.schedule(new Runnable() {
			public void run() {
				insertAdminProfiles();
			}
			
		}, 30, TimeUnit.SECONDS);
		
		executor.shutdown();
	}
	
	private void checkUserCollections() {
		logger.debug("Checking user collections");
		
		try {
			for (UserProfile user:dao.search(new UserProfileSearchCriteria())) {
				CollectionSearchCriteria crit = new CollectionSearchCriteria();
				crit.setOwnerId(user.getResourceId());
				crit.setRoot(true);
				crit.setIsContainer(true);
				
				if (collectionServiceLocator.getService().searchCollections(crit).size() == 0) {
					logger.debug("User " + user.getEmail() + " doesn't have a root collection folder. Creating one");
					Collection col = new Collection();
				
					col.setContainer(true);
					col.setFather(null);
					col.setDescription(Arrays.asList("Root folder of my collections of documents."));
					col.setName("My collections");
					col.setSubject("My collections");
					col.setFrozen(true);
					col.setOwner(user.getResourceId());
					col.setPrivate(true);
					col.setQuery(null);
					col.setVisible(true);
				
					collectionServiceLocator.getService().createCollection(col);
				}
			}
		} catch (CollectionServiceException e) {
			logger.error("Error creating collection", e);
		} catch (DAOException e) {
			logger.error("Error getting user list", e);
		}				
	}

	private void insertAdminProfiles() {
		if (adminEmails != null && !adminEmails.trim().equals("")) {
			String emails[] = adminEmails.split(",");

			for (String adminEmail : emails) {
				try {
					adminEmail = adminEmail.trim();
					logger.debug("Searching for existing admin account: "
							+ adminEmail);
					UserProfileSearchCriteria crit = new UserProfileSearchCriteria();
					UserProfile admin = null;

					crit.setEmail(adminEmail);

					List<UserProfile> list = dao.search(crit);

					if (list.size() == 0) {
						logger.debug("admin email not found. Creating new");
						String password = new Random(System.currentTimeMillis())
								.nextLong()
								+ "";
						admin = new UserProfile();

						admin.setEmail(adminEmail);
						logger.debug("new random password:" + password);

						admin.setActive(true);
						admin.setActivationId(password);
						admin.setPassword(password);
					} else {
						logger.debug("admin email found.");
						admin = list.get(0);
					}

					if (!admin.getIsSuperUser()) {
						logger.debug("adding roles to admin profile");

						List<String> roles = admin.getRoles();
						roles.add("superUser");
						roles.add("user");
						roles.add("userManager");
						roles.add("communityManager");
						roles.add("collectionManager");

						admin.setRoles(roles);

						logger.debug("Saving admin profile");

						this.dao.save(admin);
					}
				} catch (DAOException e) {
					logger.error("Error creating admin account!", e);
				}
			}
		}
	}

	public List<UserProfile> getAllUserProfiles()
			throws UserProfileServiceException {
		Thread currentThread = Thread.currentThread();
		String threadName = currentThread.getName();

		currentThread.setName("getAllUserProfiles");

		try {
			return dao.search(new UserProfileSearchCriteria());
		} catch (Exception e) {
			logger.error("Error getting profiles", e);
			throw new UserProfileServiceException("Error getting profiles");
		} finally {
			currentThread.setName(threadName);
		}
	}

	public void deleteUser(UserProfile profile)
			throws UserProfileServiceException {
		Thread currentThread = Thread.currentThread();
		String threadName = currentThread.getName();

		currentThread.setName("deleteUser");

		try {
			dao.delete(profile);
		} catch (Exception e) {
			logger.error("Error deleting profile", e);
			throw new UserProfileServiceException("Error deleting user profile");
		} finally {
			currentThread.setName(threadName);
		}
	}

	public void deleteUserById(String id) throws UserProfileServiceException {
		Thread currentThread = Thread.currentThread();
		String threadName = currentThread.getName();

		currentThread.setName("deleteUserById");

		try {
			UserProfile profile = this.getUserById(id);

			this.deleteUser(profile);
		} catch (Exception e) {
			logger.error("Error deleting profile", e);
			throw new UserProfileServiceException("Error deleting user by id");
		} finally {
			currentThread.setName(threadName);
		}
	}

	public UserProfile getUserById(String id)
			throws UserProfileServiceException {
		Thread currentThread = Thread.currentThread();
		String threadName = currentThread.getName();

		currentThread.setName("getUserById");

		try {
			return dao.getById(id);
		} catch (Exception e) {
			logger.error("Error getting profile", e);
			throw new UserProfileServiceException("Error getting user by id");
		} finally {
			currentThread.setName(threadName);
		}
	}

	public List<UserProfile> getUsersSubscribedInCommunity(String communityId)
			throws UserProfileServiceException {
		Thread currentThread = Thread.currentThread();
		String threadName = currentThread.getName();

		currentThread.setName("getUsersSubscribedInCommunity");
		try {
			UserProfileSearchCriteria criteria = new UserProfileSearchCriteria();
			criteria.setBelongsToCommunity(communityId);

			return ((UserProfileDAO) dao).search(criteria);
		} catch (Exception e) {
			logger.error("Error getting users in community", e);
			throw new UserProfileServiceException(
					"Error getting users of community");
		} finally {
			currentThread.setName(threadName);
		}

	}

	public UserProfile saveUser(UserProfile profile)
			throws UserProfileServiceException {
		Thread currentThread = Thread.currentThread();
		String threadName = currentThread.getName();

		currentThread.setName("saveUser");

		try {
			String email = profile.getEmail();

			if (email == null) {
				throw new UserProfileServiceException("Email is null");
			} else if (!Pattern.compile(".+@.+\\.[a-z]+").matcher(email)
					.matches()) {
				throw new UserProfileServiceException(
						"Email is not well formed");
			} else if (profile.getResourceId() == null) {
				logger.debug("Searching for existing user with the same email");
				UserProfileSearchCriteria crit = new UserProfileSearchCriteria();

				crit.setEmail(email);

				List<UserProfile> profiles = dao.search(crit);

				if (!profiles.isEmpty()) {
					logger.error("Cannot save new user. A user profile with the same email already exists.");

					throw new UserProfileServiceException("A user profile with the email '" + email	+ "' already exists");
				} else {
					logger.debug("No user with the same email exists");
				}
			}

			return dao.save(profile);
		} catch (DAOException e) {
			throw new UserProfileServiceException("Error saving user", e);
		} finally {
			currentThread.setName(threadName);
		}
	}

	public List<String> searchUserIds(UserProfileSearchCriteria criteria)
			throws UserProfileServiceException {
		Thread currentThread = Thread.currentThread();
		String threadName = currentThread.getName();

		currentThread.setName("searchUserIds");

		try {
			List<UserProfile> profiles = dao.search(criteria);

			List<String> ids = new ArrayList<String>();

			for (UserProfile profile : profiles)
				ids.add(profile.getResourceId());

			return ids;
		} catch (Exception e) {
			logger.error("Error searching users", e);
			throw new UserProfileServiceException("Error searching user ids");
		} finally {
			currentThread.setName(threadName);
		}

	}

	public List<UserProfile> searchUsers(UserProfileSearchCriteria criteria)
			throws UserProfileServiceException {
		Thread currentThread = Thread.currentThread();
		String threadName = currentThread.getName();

		currentThread.setName("searchUsers");

		try {
			logger.debug("Searching for users");

			return dao.search(criteria);
		} catch (Exception e) {
			logger.error("Error searching users", e);
			throw new UserProfileServiceException("Error searching users");
		} finally {
			currentThread.setName(threadName);
		}
	}

	public String getAdminEmails() {
		return adminEmails;
	}

	public void setAdminEmails(String adminEmails) {
		this.adminEmails = adminEmails;
	}

	public void setDao(UserProfileDAO dao) {
		this.dao = dao;
	}

	public void setCollectionServiceLocator(
			ServiceLocator<CollectionService> collectionServiceLocator) {
		this.collectionServiceLocator = collectionServiceLocator;
	}
}
