package eu.dnetlib.oai.utils;

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

import eu.dnetlib.oai.conf.OAIConfigurationReader;
import eu.dnetlib.oai.info.SetInfo;
import eu.dnetlib.oai.mongo.MongoPublisherStore;
import eu.dnetlib.oai.mongo.MongoPublisherStoreDAO;
import eu.dnetlib.oai.sets.MongoSetCollection;
import eu.dnetlib.rmi.provision.MDFInfo;
import eu.dnetlib.rmi.provision.OaiPublisherRuntimeException;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * Created by alessia on 18/01/17.
 */
public class OAIHelper {
	private static final Log log = LogFactory.getLog(OAIHelper.class); // NOPMD by marko on 11/24/08 5:02 PM

	@Resource(name = "oaiConfigurationExistReader")
	private OAIConfigurationReader configurationReader;
	@Resource
	private MongoPublisherStoreDAO mongoPublisherStoreDAO;
	@Resource
	private MongoSetCollection mongoSetCollection;

	public void loadConfiguration(final String dbName) {
		loadConfigurationSets(dbName);
	}

	private void loadConfigurationSets(final String dbName) {
		log.debug("*****Dropping and re-creating configuration sets******");
		this.mongoSetCollection.dropConfigurationSets(dbName);
		List<SetInfo> oaiConfigSets = configurationReader.getSets();
		for (SetInfo setInfo : oaiConfigSets) {
			this.mongoSetCollection.upsertSet(setInfo, true, dbName);
		}
		log.info("Configuration Sets updated succesfully on db: " + dbName);
	}

	/*
	 * Helper method to count elements in OAI sets.
	 */
	public void updateAllCounts(final MDFInfo mdFormat, final String dbName) {
		this.mongoSetCollection.ensureIndexes(dbName);

		MongoPublisherStore store = getStore(mdFormat, dbName);
		if (store == null) throw new OaiPublisherRuntimeException("Can't count elements for not yet created store (" + mdFormat + ") on db " + dbName);

		updateTotalCount(store, mdFormat, dbName);

		updateProvenanceSetsCount(store, mdFormat, dbName);

		updateConfiguredSetsCount(store, mdFormat, dbName);
	}

	public void updateConfiguredSetsCount(final MDFInfo mdFormat, final String dbName) {
		this.mongoSetCollection.ensureIndexes(dbName);

		MongoPublisherStore store = getStore(mdFormat, dbName);
		if (store == null) throw new OaiPublisherRuntimeException("Can't count elements for not yet created store (" + mdFormat + ") on db " + dbName);

		updateConfiguredSetsCount(store, mdFormat, dbName);
	}

	protected void updateConfiguredSetsCount(final MongoPublisherStore store, final MDFInfo mdFormat, final String dbName) {
		List<SetInfo> sets = mongoSetCollection.getConfiguredSets(dbName);
		this.updateCountForSets(store, sets, mdFormat, dbName);
	}

	protected void updateProvenanceSetsCount(final MongoPublisherStore store, final MDFInfo mdFormat, final String dbName) {
		// now we need to get all distinct set names in the store:
		List<String> distinctSetSpecs = store.getDistinctSetNamesFromRecords();
		store.upsertSets(distinctSetSpecs);
		List<SetInfo> sets = mongoSetCollection.getSetsFromData(dbName);
		this.updateCountForSets(store, sets, mdFormat, dbName);
	}

	protected void updateTotalCount(final MongoPublisherStore store, final MDFInfo mdFormat, final String dbName) {
		String baseQuery = mdFormat.getBaseQuery();
		int total = store.count(baseQuery);
		mongoSetCollection.updateCounts("ALL", mdFormat.getPrefix(), total, dbName);
		log.info("Got total for " + mdFormat.getPrefix() + " with query: " + baseQuery + " on db " + dbName);
	}

	protected void updateCountForSets(final MongoPublisherStore store, final List<SetInfo> oaiSets, final MDFInfo mdFormat, final String dbName) {

		String baseQuery = mdFormat.getBaseQuery();
		boolean hasBaseQuery = !StringUtils.isBlank(baseQuery);
		for (SetInfo setInfo : oaiSets) {
			String setQuery = "(" + setInfo.getQuery() + ")";
			if (hasBaseQuery) {
				setQuery += " AND (" + baseQuery + ")";
			}
			log.info("Counting total for " + mdFormat.getPrefix() + " set " + setInfo + " with query: " + setQuery + " on db " + dbName);
			int setCount = store.count(setQuery);
			mongoSetCollection.updateCounts(setInfo.getSetSpec(), mdFormat.getPrefix(), setCount, dbName);
		}
	}

	private MongoPublisherStore getStore(final MDFInfo mdFormat, final String dbName) {
		String format = mdFormat.getSourceFormat();
		String layout = mdFormat.getSourceLayout();
		String interpretation = mdFormat.getSourceInterpretation();
		String sourceKey = format + "-" + layout + "-" + interpretation;
		MongoPublisherStore store = this.mongoPublisherStoreDAO.getStore(format, interpretation, layout, dbName);
		log.info("Got OAI store " + sourceKey + " via metadata prefix " + mdFormat.getPrefix() + " on db " + dbName);

		return store;
	}

	public OAIConfigurationReader getConfigurationReader() {
		return configurationReader;
	}

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

	public MongoPublisherStoreDAO getMongoPublisherStoreDAO() {
		return mongoPublisherStoreDAO;
	}

	public void setMongoPublisherStoreDAO(final MongoPublisherStoreDAO mongoPublisherStoreDAO) {
		this.mongoPublisherStoreDAO = mongoPublisherStoreDAO;
	}

	public MongoSetCollection getMongoSetCollection() {
		return mongoSetCollection;
	}

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