package eu.dnetlib.enabling.is.sn;

import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import javax.xml.ws.wsaddressing.W3CEndpointReference;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Required;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;

import eu.dnetlib.enabling.is.sn.resourcestate.ResourceStateSubscription;
import eu.dnetlib.enabling.is.sn.resourcestate.ResourceStateSubscriptionRegistry;
import eu.dnetlib.enabling.is.sn.rmi.ISSNException;
import eu.dnetlib.enabling.is.sn.rmi.SubscriptionRequestRejectedException;
import eu.dnetlib.enabling.tools.UniqueIdentifierGenerator;
import eu.dnetlib.enabling.tools.UniqueIdentifierGeneratorImpl;
import eu.dnetlib.miscutils.datetime.DateUtils;

/**
 * Core business logic of ISSN service, decouple from compatibility RMI interface.
 * 
 * @author marko
 * 
 */
public class ISSNServiceCore {

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

	/**
	 * subscription registries to lookup.
	 */
	private SubscriptionRegistry registry;

	/**
	 * generates subscription identifiers.
	 */
	private UniqueIdentifierGenerator uuidGenerator = new UniqueIdentifierGeneratorImpl("sn");
	
	/**
	 * Directory in which backups are saved.
	 */
	private String backupDir;	

	/**
	 * subscribe a consumer to topics matching the topic expression.
	 * 
	 * @param consumerReference
	 *            EPR to the service to be notified
	 * @param topicExpression
	 *            topic expression
	 * @param ttl
	 *            time to live in seconds
	 * @return subscription identifier
	 */
	public String subscribe(final W3CEndpointReference consumerReference, final String topicExpression, final int ttl) throws SubscriptionRequestRejectedException {
		log.debug("subscribing");
		final SubscriptionRequest srq = new SubscriptionRequest(uuidGenerator.generateIdentifier(), consumerReference, topicExpression, ttl); // NOPMD

		final String subId = getRegistry().registerSubscription(srq);
		if (subId != null) {
			return subId;
		}
		return null;
	}

	/**
	 * Unsubscribe a subscription, by id.
	 * 
	 * @param subscrId subscription id
	 * @return true if unsubscribed
	 */
	public boolean unsubscribe(final String subscrId) {
		return getRegistry().unsubscribe(subscrId);
	}
	
	public String backup() throws ISSNException {
		log.info("Starting backup...");
		try {
			
			verifyBackupDir();

			String seq = (new SimpleDateFormat("yyyyMMdd-HHmm")).format(new Date());

			ZipOutputStream zip = new ZipOutputStream(new FileOutputStream(backupDir + "/data-" + seq + ".zip")); 

			FileWriter logFile = new FileWriter(backupDir + "/report-" + seq + ".log");
			logFile.write("Backup started at: " + DateUtils.now_ISO8601() + "\n\n");
		
			backup(zip, logFile);
			
			logFile.write("\nBackup finished at: " + DateUtils.now_ISO8601() + "\n");

			logFile.flush();
			logFile.close();

			zip.flush();
			zip.close();

			log.info("Backup finished");
			return backupDir;
		} catch (final Exception e) {
			log.error("Backup failed", e);
			throw new ISSNException("cannot backup", e);
		}
	}

	private void backup(ZipOutputStream zip, FileWriter logFile) throws IOException {

			for (ResourceStateSubscription sub : getRegistry().listSubscriptions()) {
				
				logFile.write("SUBSCRIPTION: " + sub.getSubscriptionId() + "\n");
				log.info("Backup of subscription: " + sub.getSubscriptionId());
				
				final Map<String, String> attrs = new HashMap<String, String>(); // NOPMD
				attrs.put("prefix", sub.getPrefix());
				attrs.put("type", sub.getType());
				attrs.put("resourceId", sub.getResourceId());
				attrs.put("xpath", sub.getXpath());
				attrs.put("id", sub.getSubscriptionId());
				attrs.put("subscriber", sub.getSubscriber().toString());
				
				zip.putNextEntry(new ZipEntry(sub.getSubscriptionId()));
				zip.write(encodeJSon(attrs).getBytes());
				zip.closeEntry();
			}
	}
	
	private String encodeJSon(final Map<String, String> map) {
        return new Gson().toJson(
                        map, 
                        new TypeToken<Map<String, String>>() {}.getType()
        );
	}
	
	private void verifyBackupDir() {
		File d = new File(backupDir);
		if (!d.exists()) d.mkdirs();
	}	
	
	public UniqueIdentifierGenerator getUuidGenerator() {
		return uuidGenerator;
	}

	public void setUuidGenerator(final UniqueIdentifierGenerator uuidGenerator) {
		this.uuidGenerator = uuidGenerator;
	}

	public String getBackupDir() {
		return backupDir;
	}

	@Required
	public void setBackupDir(String backupDir) {
		this.backupDir = backupDir;
	}

	public SubscriptionRegistry getRegistry() {
		return registry;
	}

	public void setRegistry(final SubscriptionRegistry registry) {
		this.registry = registry;
	}
}
