package eu.dnetlib.oai.sets;

import java.util.List;
import java.util.concurrent.Callable;
import javax.annotation.Resource;

import com.google.common.collect.Lists;
import eu.dnetlib.oai.conf.OAIConfigurationReader;
import eu.dnetlib.oai.utils.OAIHelper;
import eu.dnetlib.rmi.provision.MDFInfo;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * Updates the counts for all sets in the special sets collection of OAI store.
 *
 * @author alessia
 */
public class OAISetsCounter {

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

	@Resource(name = "oaiConfigurationExistReader")
	private OAIConfigurationReader configurationReader;
	@Resource
	private MongoSetCollection mongoSetCollection;
	@Resource
	private OAIHelper oaiHelper;

	/**
	 * Update counts for all OAI sets.
	 * <p>
	 * If a non blank storeId is given, counts are updated only for those mdPreromfix served by the given store.
	 * </p>
	 * <p>
	 * Otherwise all counts are updated.
	 * </p>
	 *
	 * @param storeId
	 *            oai store id. Can be blank to execute over all metadata served by OAI.
	 * @param callback
	 *            callback to execute when the execution is successful
	 * @param failCallback
	 *            to execute when the execution fails
	 */
	public void updateCounts(final String storeId, final String dbName, final Callable<?> callback, final Callable<?> failCallback) {
		new Thread() {
			@Override
			public void run() {
				try {
					List<MDFInfo> metadataFormats = listProcessableMDFInfo(storeId);
					for (MDFInfo mdFormat : metadataFormats) {
						oaiHelper.updateAllCounts(mdFormat, dbName);
					}
					log.info("All sets count updated succesfully on db: " + dbName);
					executeCallback(callback);
				} catch (Exception e) {
					log.error(e);
					executeCallback(failCallback);
				}
			}
		}.start();
	}

	public void updateCounts(final MDFInfo mdf, final String dbName, final Callable<?> callback, final Callable<?> failCallback) {
		new Thread(() -> {
			try {
				oaiHelper.updateAllCounts(mdf, dbName);
				log.info("All sets count updated succesfully for metadata format " + mdf + " on db " + dbName);
				executeCallback(callback);
			} catch (Exception e) {
				log.error(e);
				executeCallback(failCallback);
			}
		}).start();
	}

	/**
	 * Update counts for configured OAI sets and all md formats.
	 *
	 * @param callback
	 *            callback to execute when the execution is successful
	 * @param failCallback
	 *            to execute when the execution fails
	 */
	public void updateConfigurationCounts(final String storeId, final String dbName, final Callable<?> callback, final Callable<?> failCallback) {
		new Thread(() -> {
				try {
					List<MDFInfo> metadataFormats = listProcessableMDFInfo(storeId);

					for (MDFInfo mdFormat : metadataFormats) {
						oaiHelper.updateConfiguredSetsCount(mdFormat, dbName);
					}
					log.info("All configured sets count updated succesfully on db " + dbName);
					executeCallback(callback);
				} catch (Exception e) {
					log.error(e);
					executeCallback(failCallback);
				}
		}).start();
	}

	protected void executeCallback(final Callable<?> callback) {
		if (callback != null) {
			try {
				callback.call();
			} catch (Exception e) {
				log.error("Error executing callback", e);
			}
		}
	}

	protected List<MDFInfo> listProcessableMDFInfo(final String storeId) {
		List<MDFInfo> metadataFormats = Lists.newArrayList();
		if (StringUtils.isBlank(storeId)) {
			metadataFormats = configurationReader.getMetadataFormatInfo();
		} else {
			String[] splitted = storeId.split("-");
			String format = splitted[0];
			String layout = splitted[1];
			String inter = splitted[2];
			metadataFormats = configurationReader.getFormatsServedBy(format, layout, inter);
		}
		return metadataFormats;
	}

	public OAIConfigurationReader getConfigurationReader() {
		return configurationReader;
	}

	public void setConfigurationReader(final OAIConfigurationReader configurationReader) {
		this.configurationReader = configurationReader;
	}

	public MongoSetCollection getMongoSetCollection() {
		return mongoSetCollection;
	}

	public void setMongoSetCollection(final MongoSetCollection mongoSetCollection) {
		this.mongoSetCollection = mongoSetCollection;
	}

	public OAIHelper getOaiSetCounterHelper() {
		return oaiHelper;
	}

	public void setOaiSetCounterHelper(final OAIHelper oaiHelper) {
		this.oaiHelper = oaiHelper;
	}

}
