package eu.dnetlib.data.objectstore.modular;

import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import eu.dnetlib.data.objectstore.modular.connector.ObjectStore;
import eu.dnetlib.data.objectstore.modular.connector.ObjectStoreDao;
import eu.dnetlib.data.objectstore.rmi.MetadataObjectRecord;
import eu.dnetlib.data.objectstore.rmi.ObjectStoreFile;
import eu.dnetlib.data.objectstore.rmi.ObjectStoreServiceException;
import eu.dnetlib.data.objectstore.rmi.Protocols;
import eu.dnetlib.enabling.is.registry.rmi.ISRegistryService;
import eu.dnetlib.enabling.locators.UniqueServiceLocator;
import eu.dnetlib.enabling.resultset.client.ResultSetClientFactory;
import eu.dnetlib.miscutils.datetime.DateUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Required;

/**
 * The Class ModularObjectStoreFeeder, responsible to feed data into the object Store
 */
public class ModularObjectStoreFeeder {

	private static final Log log = LogFactory.getLog(ModularObjectStoreFeeder.class);

	/** The dao of the objectStore. */
	private ObjectStoreDao dao;

	/** The result set client factory. */
	private ResultSetClientFactory resultSetClientFactory;

	/** The service locator. */
	private UniqueServiceLocator serviceLocator;

	/**
	 * Gets the dao.
	 *
	 * @return the dao
	 */
	public ObjectStoreDao getDao() {
		return dao;
	}

	/**
	 * Sets the dao.
	 *
	 * @param dao
	 *            the new dao
	 */
	@Required
	public void setDao(final ObjectStoreDao dao) {
		this.dao = dao;
	}

	/**
	 * Gets the result set client factory.
	 *
	 * @return the result set client factory
	 */
	public ResultSetClientFactory getResultSetClientFactory() {
		return resultSetClientFactory;
	}

	/**
	 * Sets the result set client factory.
	 *
	 * @param resultSetClientFactory
	 *            the new result set client factory
	 */

	@Required
	public void setResultSetClientFactory(final ResultSetClientFactory resultSetClientFactory) {
		this.resultSetClientFactory = resultSetClientFactory;
	}

	/**
	 * Feed metadata object record.
	 *
	 * @param objectStoreID
	 *            the object store id
	 * @param rsEpr
	 *            the rs epr
	 * @param mime
	 *            the mime
	 * @throws ObjectStoreServiceException
	 */
	public int feedMetadataObjectRecord(final String objectStoreID, final String rsEpr, final String mime) throws ObjectStoreServiceException {

		final Iterable<String> records = resultSetClientFactory.getClient(rsEpr);
		Iterable<MetadataObjectRecord> toIngest = Iterables.transform(records, input -> {
			MetadataObjectRecord record = MetadataObjectRecord.initFromJson(input);
			if (record != null) {
				record.setMime(mime);
			} else {
				log.error("An input record is null :" + input);
			}
			return record;
		});
		ObjectStore store = dao.getObjectStore(objectStoreID);
		int size = store.feedMetadataRecord(toIngest, true);
		touch(objectStoreID, size);
		return size;
	}

	/**
	 * Feed object in the object store starting from an EPR of objectMetadata
	 *
	 * @param obsId
	 *            the objectStore id
	 * @param rsEpr
	 *            the result set Endpoint-reference
	 * @param protocol
	 *            the protocol
	 * @param login
	 *            the login
	 * @param password
	 *            the password
	 * @param mime
	 *            the mime type
	 * @throws ObjectStoreServiceException
	 */
	public void feed(final String obsId, final String rsEpr, final Protocols protocol, final String login, final String password, final String mime)
			throws ObjectStoreServiceException {
		final Iterable<String> records = resultSetClientFactory.getClient(rsEpr);
		ObjectBroker objectBroker = new ObjectBroker(protocol, login, password, mime);
		Iterable<ObjectStoreRecord> toIngest = Iterables.transform(records, objectBroker);
		ObjectStore store = dao.getObjectStore(obsId);
		int size = store.feed(toIngest, true);
		touch(obsId, size);
	}

	/**
	 * Feed a single object in the object Stroe.
	 *
	 * @param objectStoreID
	 *            the object store id
	 * @param objectID
	 *            the object id
	 * @param URIObject
	 *            the URI of object
	 * @param protocol
	 *            the protocol
	 * @param login
	 *            the login
	 * @param password
	 *            the password
	 * @param mime
	 *            the mime type
	 * @throws ObjectStoreServiceException
	 */
	public void feedObject(final String objectStoreID,
			final String objectID,
			final String URIObject,
			final Protocols protocol,
			final String login,
			final String password,
			final String mime) throws ObjectStoreServiceException {
		ObjectStoreFile inputFile = new ObjectStoreFile();
		inputFile.setURI(URIObject);
		inputFile.setObjectID(objectID);
		ObjectBroker objectBroker = new ObjectBroker(protocol, login, password, mime);
		Iterable<ObjectStoreRecord> toIngest = Iterables.transform(Lists.newArrayList(inputFile.toJSON()), objectBroker);
		ObjectStore store = dao.getObjectStore(objectStoreID);
		int size = store.feed(toIngest, true);
		touch(objectStoreID, size);
	}

	/**
	 * Feed object record.
	 *
	 * @param objectStoreID
	 *            the object store id
	 * @param record
	 *            the record
	 * @return the string
	 * @throws ObjectStoreServiceException
	 */
	public String feedObjectRecord(final String objectStoreID, final ObjectStoreRecord record) throws ObjectStoreServiceException {
		ObjectStore store = dao.getObjectStore(objectStoreID);
		return store.feedObjectRecord(record);
	}

	/**
	 * Sets the last modified date in the profile.
	 *
	 * @param obsId
	 *            the obs id
	 * @param size
	 *            the size
	 */
	public void touch(final String obsId, final int size) {
		try {
			final String now = DateUtils.now_ISO8601();

			final String mdstoreXUpdate = "for $x in //RESOURCE_PROFILE[.//RESOURCE_IDENTIFIER/@value = '" + obsId + "']"
					+ "return update value $x//LAST_STORAGE_DATE with '" + now + "'";

			serviceLocator.getService(ISRegistryService.class, true).executeXUpdate(mdstoreXUpdate);

			touchSize(obsId, size);
		} catch (final Exception e) {
			throw new IllegalStateException(e);
		}
	}

	/**
	 * Touch size.
	 *
	 * @param obsId
	 *            the obs id
	 * @param size
	 *            the size
	 */
	public void touchSize(final String obsId, final int size) {
		try {
			final String mdstoreNumberXUpdate = "for $x in //RESOURCE_PROFILE[.//RESOURCE_IDENTIFIER/@value = '" + obsId + "']"
					+ "return update value $x//COUNT_STORE with '" + size + "'";

			serviceLocator.getService(ISRegistryService.class, true).executeXUpdate(mdstoreNumberXUpdate);
		} catch (final Exception e) {
			throw new IllegalStateException(e);
		}
	}

	public UniqueServiceLocator getServiceLocator() {
		return serviceLocator;
	}

	@Required
	public void setServiceLocator(final UniqueServiceLocator serviceLocator) {
		this.serviceLocator = serviceLocator;
	}

}
