package eu.dnetlib.enabling.is.registry;

import java.util.Date;

import javax.annotation.Resource;
import javax.xml.xpath.XPathExpressionException;

import org.springframework.beans.factory.annotation.Required;

import eu.dnetlib.enabling.is.registry.rmi.ISRegistryException;
import eu.dnetlib.enabling.is.registry.rmi.ISRegistryService;
import eu.dnetlib.enabling.is.registry.schema.OpaqueResourceValidator;
import eu.dnetlib.enabling.is.registry.schema.ValidationException;
import eu.dnetlib.enabling.is.registry.validation.ProfileValidationStrategy;
import eu.dnetlib.enabling.is.registry.validation.RegistrationPhase;
import eu.dnetlib.enabling.is.store.rmi.ISStoreException;
import eu.dnetlib.enabling.is.store.rmi.ISStoreService;
import eu.dnetlib.enabling.tools.CompatResourceIdentifierResolverImpl;
import eu.dnetlib.enabling.tools.OpaqueResource;
import eu.dnetlib.enabling.tools.ResourceIdentifierComposer;
import eu.dnetlib.enabling.tools.ResourceIdentifierResolver;
import eu.dnetlib.enabling.tools.XQueryUtils;

public class IdPreservingPendingResourceManagerImpl implements PendingResourceManager, ResourceKindResolver {

	/**
	 * is registry
	 */
	private ISRegistryService isRegistry;

	/**
	 * is store
	 */
	private ISStoreService isStore;

	/**
	 * This bean resolver
	 */
	private ResourceKindResolver resourceKindResolver;

	/**
	 * used to validate resources.
	 */
	private OpaqueResourceValidator resourceValidator;

	/**
	 * used to get the root collection.
	 */
	private XQueryUtils xqueryUtils;

	/**
	 * manages resource identifier mappings with the abstract xmldb namespace (files/collections).
	 */
	@Resource
	private ResourceIdentifierResolver resIdResolver = new CompatResourceIdentifierResolverImpl();

	/**
	 * used to create a new resource id.
	 */
	@Resource
	private ResourceIdentifierComposer resIdComposer = new CompatResourceIdentifierResolverImpl();

	/**
	 * used to validate resources w.r.t. a set of defined properties.
	 */
	@Resource
	private ProfileValidationStrategy profileValidationStrategy;

	/**
	 * {@inheritDoc}
	 * 
	 * @see eu.dnetlib.enabling.is.registry.PendingResourceManager#setPending(eu.dnetlib.enabling.tools.OpaqueResource)
	 */
	@Override
	public void setPending(final OpaqueResource resource, final boolean local) {
		try {
			resource.setResourceKind(getPendingKindForType(resource.getResourceType()));

			if (!local) {
				isRegistry.deleteProfile(resource.getResourceId());

				registerProfile(resource);
			}

		} catch (XPathExpressionException e) {
			throw new IllegalStateException(e);
		} catch (ISRegistryException e) {
			throw new IllegalStateException(e);
		}
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see eu.dnetlib.enabling.is.registry.PendingResourceManager#setPending(eu.dnetlib.enabling.tools.OpaqueResource)
	 */
	@Override
	public void setPending(final OpaqueResource resource) {
		setPending(resource, false);
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @see eu.dnetlib.enabling.is.registry.PendingResourceManager#setValid(eu.dnetlib.enabling.tools.OpaqueResource)
	 */
	@Override
	public void setValid(final OpaqueResource resource) {
		try {
			if (resource.getResourceKind() != null && resource.getResourceKind().equals(getNormalKindForType(resource.getResourceType()))) { throw new IllegalArgumentException(
					"trying to validate an already valid resource"); }

			profileValidationStrategy.accept(resource, RegistrationPhase.Validate);

			resource.setResourceKind(getNormalKindForType(resource.getResourceType()));

			isRegistry.deleteProfile(resource.getResourceId());

			registerProfile(resource);

		} catch (XPathExpressionException e) {
			throw new IllegalStateException(e);
		} catch (ISRegistryException e) {
			throw new IllegalStateException(e);
		} catch (ValidationException e) {
			throw new IllegalStateException(e);
		}
	}

	public String registerProfile(final OpaqueResource resource) throws ISRegistryException {
		try {
			final String oldId = resource.getResourceId();
			final String fileName = resIdResolver.getFileName(oldId);
			final String newColl = xqueryUtils.getCollectionPath(resource);

			final String newId = resIdComposer.createResourceId(fileName, newColl);

			resource.setResourceId(newId);

			resource.setModificationDate(new Date());

			// log.info("validating to xml schema: " + resource.asString());
			resourceValidator.validate(resource);

			// TODO: factor out ResourceType class
			isStore.insertXML(fileName, xqueryUtils.getRootCollection() + newColl, resource.asString());

			return resource.getResourceId();
		} catch (ISStoreException e) {
			throw new ISRegistryException(e);
		} catch (ValidationException e) {
			throw new ISRegistryException("profile is not conforming to the schema: " + e.getMessage(), e);
		}
	}

	@Override
	public String getNormalKindForType(final String resourceType) throws XPathExpressionException {
		return resourceKindResolver.getNormalKindForType(resourceType);
	}

	@Override
	public String getPendingKindForType(final String resourceType) throws XPathExpressionException {
		return resourceKindResolver.getPendingKindForType(resourceType);
	}

	// /////////////////////////////

	public ResourceKindResolver getResourceKindResolver() {
		return resourceKindResolver;
	}

	@Required
	public void setResourceKindResolver(final ResourceKindResolver resourceKindResolver) {
		this.resourceKindResolver = resourceKindResolver;
	}

	@Required
	public void setXqueryUtils(final XQueryUtils xqueryUtils) {
		this.xqueryUtils = xqueryUtils;
	}

	public OpaqueResourceValidator getResourceValidator() {
		return resourceValidator;
	}

	@Required
	public void setResourceValidator(final OpaqueResourceValidator resourceValidator) {
		this.resourceValidator = resourceValidator;
	}

	public ISRegistryService getIsRegistry() {
		return isRegistry;
	}

	@Required
	public void setIsRegistry(final ISRegistryService isRegistry) {
		this.isRegistry = isRegistry;
	}

	public ISStoreService getIsStore() {
		return isStore;
	}

	@Required
	public void setIsStore(final ISStoreService isStore) {
		this.isStore = isStore;
	}

}
