package gr.uoa.di.web.utils;

import eu.dnetlib.api.data.SearchServiceException;
import eu.dnetlib.domain.ActionType;
import eu.dnetlib.domain.ResourceType;
import eu.dnetlib.domain.data.Repository;
import eu.dnetlib.domain.data.RepositorySearchCriteria;
import eu.dnetlib.domain.enabling.Notification;
import gr.uoa.di.driver.app.DriverServiceImpl;
import gr.uoa.di.driver.enabling.ISLookUp;
import gr.uoa.di.driver.enabling.ISLookUpException;
import gr.uoa.di.driver.enabling.issn.NotificationListener;
import gr.uoa.di.web.utils.search.SearchManager;

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import org.apache.log4j.Logger;

public class DriverStatus implements NotificationListener {

	private long documentCount = 0;
	private int repositoryCount = 0;
	private int countryCount = 0;
	private int documentCountGranularity = 10000;
	private boolean enableUpdate = true;

	private SearchManager searchManager = null;
	private ISLookUp<Repository> repositoryLookUp = null;

	private DriverServiceImpl serviceImpl = null;
	
	private ScheduledExecutorService executor = null;
	
	private static Logger logger = Logger.getLogger(DriverStatus.class);

	public DriverStatus(long documentCount, int repositoryCount, int countryCount, boolean enableUpdate) {
		super();
		
		this.documentCount = documentCount;
		this.repositoryCount = repositoryCount;
		this.countryCount = countryCount;
		this.enableUpdate = enableUpdate;
	}

	public void init() {
		serviceImpl.subscribe(ActionType.CREATE, ResourceType.INDEXDSRESOURCETYPE, this);
		serviceImpl.subscribe(ActionType.UPDATE, ResourceType.INDEXDSRESOURCETYPE, this);
		serviceImpl.subscribe(ActionType.DELETE, ResourceType.INDEXDSRESOURCETYPE, this);
		serviceImpl.subscribe(ActionType.CREATE, ResourceType.REPOSITORYSERVICERESOURCETYPE, this);
		serviceImpl.subscribe(ActionType.UPDATE, ResourceType.REPOSITORYSERVICERESOURCETYPE, this);
		serviceImpl.subscribe(ActionType.DELETE, ResourceType.REPOSITORYSERVICERESOURCETYPE, this);
		
		this.executor = Executors.newSingleThreadScheduledExecutor();
		this.executor.scheduleWithFixedDelay(new Updater(), 1, 30, TimeUnit.MINUTES);
	}
	
	public void destroy() {
		this.executor.shutdown();
	}

	private void updateStatus() {
		if (enableUpdate) {
			logger.debug("updating driver status...");
			try {
				int count = searchManager.search("textual", 1, 1)
						.getNumberOfDocuments();
	
				List<Repository> repositories = repositoryLookUp.fetch(new RepositorySearchCriteria());
				Set<String> countries = new HashSet<String>();
				
				for (Repository repository : repositories) {
					countries.add(repository.getCountry());
				}
				
				documentCount = count - count % documentCountGranularity;
				repositoryCount = repositories.size();
				countryCount = countries.size(); 
	
			} catch (SearchServiceException sse) {
				logger.error("Error updating document count", sse);
				
			} catch (ISLookUpException isle) {
				logger.error("Error updating repository and country count", isle);
			} catch (Exception e) {
				logger.error("Error updating driver status. Using previous or default data");
			}
		}
	}

	@Override
	public void processNotification(Notification notification) {
		if (notification.getResourceType().equals(ResourceType.INDEXDSRESOURCETYPE)
				|| notification.getResourceType().equals(ResourceType.REPOSITORYSERVICERESOURCETYPE)){
			
			logger.debug("Updating status");
			updateStatus();
		}
	}

	public SearchManager getSearchManager() {
		return searchManager;
	}

	public void setSearchManager(SearchManager searchManager) {
		this.searchManager = searchManager;
	}

	public ISLookUp<Repository> getRepositoryLookUp() {
		return repositoryLookUp;
	}

	public void setRepositoryLookUp(ISLookUp<Repository> repositoryLookUp) {
		this.repositoryLookUp = repositoryLookUp;
	}

	public long getDocumentCount() {
		return documentCount;
	}

	public int getRepositoryCount() {
		return repositoryCount;
	}

	public int getCountryCount() {
		return countryCount;
	}

	public DriverServiceImpl getServiceImpl() {
		return serviceImpl;
	}

	public void setServiceImpl(DriverServiceImpl serviceImpl) {
		this.serviceImpl = serviceImpl;
	}

	public int getDocumentCountGranularity() {
		return documentCountGranularity;
	}

	public void setDocumentCountGranularity(int documentCountGranularity) {
		this.documentCountGranularity = documentCountGranularity;
	}
	
	public void setEnableUpdate(boolean enableUpdate) {
		this.enableUpdate = enableUpdate;
	}

	class Updater implements Runnable {
		public void run() {
			try {
				updateStatus();
			} catch (Exception e) {
				logger.error("Error running update", e);
			}
		}
	}
}
