package eu.dnetlib.enabling.inspector;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.StringWriter;
import java.util.Arrays;
import java.util.Comparator;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.cxf.helpers.IOUtils;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import eu.dnetlib.enabling.is.sn.ISSNServiceCore;
import eu.dnetlib.xml.database.XMLDatabase;

/**
 * This controller offers a simple way to backup the xmldb.
 *
 * @author michele
 *
 */
@Controller
public class BackupController extends AbstractInspectorController {

	/**
	 * BUFFER size for file copy.
	 */
	private static final int BUFFER_SIZE = 4096;

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

	/**
	 * xmldb.
	 */
	@Resource(name = "existDatabase")
	private transient XMLDatabase xmlDatabase;
	
	/**
	 * is sn subscription registries.
	 */
	@Resource(name = "isSNServiceCore")
	private transient ISSNServiceCore issnServiceCore;
	
	private enum BackupType {
		profile, subscription
	}

	@RequestMapping(value = "/inspector/backups.do")
	public void backup(final Model model) {
		// only view
	}
	
	@RequestMapping(value = "/inspector/backupProfiles.do")
	public void backupProfiles(
			final Model model,
			@RequestParam(value = "exec", required = false) final String exec,
			@RequestParam(value = "delete", required = false) final String delete) {

		if (exec != null && exec.equals("1")) {
			execProfileBackup(model);
		}
		if (delete != null) {
			deleteBackup(model, xmlDatabase.getBackupDir(), delete);
		}

		listBackups(model, xmlDatabase.getBackupDir());
	}
	
	@RequestMapping(value = "/inspector/backupSubscriptions.do")
	public void backupSubscriptions(
			final Model model,
			@RequestParam(value = "exec", required = false) final String exec,
			@RequestParam(value = "delete", required = false) final String delete) {

		if (exec != null && exec.equals("1")) {
			execSubscriptionsBackup(model);
		}
		if (delete != null) {
			deleteBackup(model, issnServiceCore.getBackupDir(), delete);
		}

		listBackups(model, issnServiceCore.getBackupDir());
	}

	/**
	 * Executes a backup.
	 *
	 * @param model
	 *            mvc model
	 */
	private void execProfileBackup(final Model model) {
		try {
			xmlDatabase.backup();
			log.info("Backup done");
		} catch (final Exception e) {
			model.addAttribute("message", "Backup failed: please retry later");
			log.fatal("Backup failed", e);
		}
	}

	/**
	 * Executes a backup.
	 *
	 * @param model
	 *            mvc model
	 */
	private void execSubscriptionsBackup(final Model model) {
		try {
			issnServiceCore.backup();
			log.info("Backup done");
		} catch (final Exception e) {
			model.addAttribute("message", "Backup failed: please retry later");
			log.fatal("Backup failed", e);
		}
	}	
	
	/**
	 * Deletes a backup.
	 *
	 * @param model
	 *            mvc model
	 * @param backup
	 *            backup file name
	 */
	private void deleteBackup(final Model model, final String backupDir, final String backup) {
		try {
			final String logFile = backup.replaceFirst("data-", "report-").replace(".zip", ".log");
			(new File(backupDir + "/" + backup)).delete();
			(new File(backupDir + "/" + logFile)).delete();
			log.info("Backup deleted");
		} catch (final Exception e) {
			model.addAttribute("message", "failed: " + e.getMessage());
			log.info("Backup deletion failed: " + e.getMessage());
		}
	}

	/**
	 * List backups.
	 *
	 * @param model
	 *            mvc mode
	 */
	private void listBackups(final Model model, final String backupDir) {
		model.addAttribute("size", 0);
		
		final File dir = new File(backupDir);
		if (dir.exists() && dir.isDirectory()) {
			final FilenameFilter filter = new FilenameFilter() { // NOPMD
				@Override
				public boolean accept(final File dir, final String name) {
					return (name.startsWith("data-") && name.endsWith(".zip"));
				}
			};
			final File[] list = dir.listFiles(filter);
			if (list != null) {
				Arrays.sort(list, new Comparator<File>(){
				    @Override
					public int compare(final File f1, final File f2) {
				        return Long.valueOf(f1.lastModified()).compareTo(f2.lastModified());
				    } });				
				
				model.addAttribute("size", list.length);
				model.addAttribute("backups", list);
			}
		}
	}

	/**
	 * get a backup Log.
	 *
	 * @throws IOException
	 *             could happen
	 * @param model
	 *            mvc model
	 * @param backup
	 *            the name of backup
	 * @throws
	 */
	@RequestMapping(value = "/inspector/backupLog.do")
	void backupLog(final Model model, 
			@RequestParam(value = "backup") final String backup,
			@RequestParam(value = "type") final String type) throws IOException {
		
		final String logFile = backup.replaceFirst("data-", "report-").replace(".zip", ".log");
		
		final FileReader freader = new FileReader(getBackupDir(type) + "/" + logFile);
		final StringWriter blog = new StringWriter();

		try {
			IOUtils.copy(freader, blog, BUFFER_SIZE);
		} finally {
			freader.close();
		}

		model.addAttribute("log", blog);
		model.addAttribute("backup", backup);
	}

	/**
	 * Download a backup.
	 *
	 * @param response
	 *            response
	 * @param backup
	 *            the name of backup
	 * @param out
	 *            response stream
	 * @throws IOException
	 *             could happen
	 */
	@RequestMapping(value = "/inspector/backupDownload.do")
	void downloadBackup(
			final HttpServletResponse response,
			@RequestParam(value = "backup", required = true) final String backup,
			@RequestParam(value = "type", required = true) final String type,
			final OutputStream out) throws IOException {
		response.setContentType("application/zip");

		final String endUserFilename = backup.replaceFirst("data", "dlib-backup-" + type);
		response.addHeader("Content-disposition", "attachment; filename=" + endUserFilename);

		final InputStream backupStream = new FileInputStream(getBackupDir(type) + "/" + backup);

		try {
			IOUtils.copy(backupStream, out);
		} finally {
			backupStream.close();
		}

		out.flush();
		out.close();
	}
	
	private String getBackupDir(final String type) {
		switch(BackupType.valueOf(type)) {
			case profile:
				return xmlDatabase.getBackupDir();
			case subscription:
				return issnServiceCore.getBackupDir();
			default:
				throw new IllegalArgumentException("wrong backup type parameter: " + type);
		}
	}	
}
