package eu.dnetlib.data.mdstore.modular.inspector;

import static eu.dnetlib.miscutils.collections.MappedCollection.listMap;

import java.io.StringReader;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.io.SAXReader;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

import com.google.common.base.Function;
import com.google.common.collect.Lists;

import eu.dnetlib.data.mdstore.MDStoreServiceException;
import eu.dnetlib.data.mdstore.modular.MDStoreDescription;
import eu.dnetlib.data.mdstore.modular.MDStoreFeeder;
import eu.dnetlib.data.mdstore.modular.connector.MDStore;
import eu.dnetlib.data.mdstore.modular.connector.MDStoreDao;
import eu.dnetlib.data.mdstore.modular.connector.MDStoreManagerInfo;
import eu.dnetlib.data.mdstore.modular.connector.MDStoreTransactionManager;
import eu.dnetlib.data.mdstore.modular.mongodb.MongoMDStore;
import eu.dnetlib.enabling.inspector.AbstractInspectorController;
import eu.dnetlib.enabling.resultset.ResultSetListener;
import eu.dnetlib.miscutils.functional.xml.TryIndentXmlString;

@Controller
public class MDStoreInspector extends AbstractInspectorController {

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

	@Resource(name = "mongodbMDStoreDao")
	private MDStoreDao dao;

	@Autowired
	private MDStoreTransactionManager transactionManager;

	@Resource
	private MDStoreFeeder feeder;

	@RequestMapping(value = "/inspector/mdstores.do")
	public void mdstores(final Model model) throws MDStoreServiceException {
		model.addAttribute("mdstores", dao.listMDStores());
	}

	@RequestMapping(value = "/inspector/mdstore.do", method = RequestMethod.GET)
	public void mdstore(final Model model,
			@RequestParam("id") final String id,
			@RequestParam(value = "start", required = false) final Integer startParam,
			@RequestParam(value = "regex", required = false) final String regex) throws MDStoreServiceException {
		int pageSize = 10;
		int start = 0;

		if (startParam != null) {
			start = startParam;
		}

		MongoMDStore mdstore = (MongoMDStore) dao.getMDStore(id);

		ResultSetListener rs = mdstore.deliver(null, null, regex);
		Function<String, Map<String, String>> function = new Function<String, Map<String, String>>() {

			private SAXReader reader = new SAXReader();

			@Override
			public Map<String, String> apply(final String input) {
				try {

					Document doc = reader.read(new StringReader(input));
					final HashMap<String, String> result = new HashMap<String, String>();
					result.put("docId", doc.valueOf("//*[local-name()='objIdentifier']"));
					result.put("body", input.replaceAll("<", "&lt;").replaceAll(">", "&gt;"));
					result.put("mdId", id);
					return result;
				} catch (DocumentException e) {
					throw new RuntimeException(e);
				}

			}
		};
		List<Map<String, String>> page = Lists.transform(listMap(rs.getResult(1 + start, start + pageSize), new TryIndentXmlString()), function);

		model.addAttribute("id", id);
		model.addAttribute("start", start);
		model.addAttribute("regex", regex);
		model.addAttribute("nextPage", start + pageSize);
		model.addAttribute("prevPage", Math.max(0, start - pageSize));
		model.addAttribute("size", rs.getSize());
		model.addAttribute("page", page);
	}

	@RequestMapping(value = "/inspector/mdstore.do", method = RequestMethod.POST)
	public String bulkReplace(final Model model,
			@RequestParam("id") final String id,
			@RequestParam("regex") final String regex,
			@RequestParam("replace") final String replace,
			@RequestParam(value = "checkReplace", required = false) final Boolean checkReplace) throws MDStoreServiceException {

		log.debug("regex: " + regex);
		log.debug("replace: " + replace);

		MongoMDStore mdstore = (MongoMDStore) dao.getMDStore(id);

		boolean replaceEnable = checkReplace != null && checkReplace == true;

		if (replaceEnable) {
			mdstore.replace(regex, replace);
		} else {
			model.addAttribute("regex", regex);
		}

		return "redirect:mdstore.do?id=" + id;
	}

	@RequestMapping(value = "/inspector/mdstoreEditResult.do")
	public void mdstoreEditRecord(final Model model, @RequestParam("mdId") final String mdId, @RequestParam("docId") final String docId)
			throws MDStoreServiceException {
		MDStore mdstore = dao.getMDStore(mdId);
		String record = mdstore.getRecord(docId);
		log.debug("Displaying record for editing :" + record);
		model.addAttribute("mdId", mdId);
		TryIndentXmlString tryIndent = new TryIndentXmlString();
		String escaped = tryIndent.evaluate(record);
		model.addAttribute("body", escaped.replace("&", "&amp;"));
	}

	@RequestMapping(value = "/inspector/mdstoreSaveRecord.do")
	public String mdstoreSaveRecord(final Model model, @RequestParam("mdId") final String mdId, @RequestParam("body") final String body)
			throws MDStoreServiceException {
		MDStore mdstore = dao.getMDStore(mdId);

		mdstore.feed(Lists.newArrayList(body), true);

		return "redirect:mdstore.do?id=" + mdId;

	}

	@RequestMapping(value = "/inspector/mdstoreDeleteRecord.do")
	public String mdstoreDeleteRecord(final Model model, @RequestParam("mdId") final String mdId, @RequestParam("docId") final String docId)
			throws MDStoreServiceException {
		MDStore mdstore = dao.getMDStore(mdId);
		log.info("deleting record " + docId);
		mdstore.deleteRecord(docId);
		return "redirect:mdstore.do?id=" + mdId;
	}

	@RequestMapping(value = "/inspector/mdstoresRefreshSizes.do")
	public String mdstoresRefreshSizes(final Model model) throws MDStoreServiceException {

		for (MDStoreDescription mdstore : dao.listMDStores()) {
			feeder.touchSize(mdstore.getId(), dao.getMDStore(mdstore.getId()).getSize());
		}

		return "redirect:mdstores.do";
	}

	@RequestMapping(value = "/inspector/ensure.do")
	public String mdstoreEnsureIndex(final Model model, @RequestParam("id") final String mdId) throws MDStoreServiceException {

		MongoMDStore mdStore = (MongoMDStore) dao.getMDStore(mdId);

		log.info("manual ensureIndex for mdId: " + mdId);
		mdStore.ensureIndices();

		return "redirect:mdstores.do";
	}

	@RequestMapping(value = "/inspector/infoTransaction.do")
	public void mdstoreInfoTransaction(final Model model, @RequestParam("id") final String id) throws MDStoreServiceException {
		MDStoreManagerInfo info = transactionManager.getInfoForCurrentMdStore(id);
		model.addAttribute("info", info);
	}

	@RequestMapping(value = "/inspector/dropUsedCollection.do")
	public String dropUsedCollection(final Model model, @RequestParam("mdId") final String mdId, @RequestParam("id") final String id)
			throws MDStoreServiceException {
		transactionManager.dropUsed(mdId, id);
		return "redirect:mdstore.do?id=" + mdId;
	}

	@RequestMapping(value = "/inspector/invalidTransactionCollection.do")
	public String invalidTransactionCollection(final Model model, @RequestParam("mdId") final String mdId, @RequestParam("id") final String id)
			throws MDStoreServiceException {
		transactionManager.dropTransaction(mdId, id);
		return "redirect:mdstore.do?id=" + mdId;
	}

	@RequestMapping(value = "/inspector/refreshSizes.do")
	public String refreshSizes(final Model model) throws MDStoreServiceException {

		dao.refreshSizes();
		return "redirect:mdstores.do";
	}

	@RequestMapping(value = "/inspector/doGarbage.do")
	public String doGarbage(final Model model) throws MDStoreServiceException {

		dao.startGarbage();
		return "redirect:mdstores.do";
	}
}
