package eu.dnetlib.actionmanager.inspector;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

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

import eu.dnetlib.actionmanager.ActionManagerConstants.ACTION_TYPE;
import eu.dnetlib.actionmanager.common.Agent;
import eu.dnetlib.actionmanager.common.Agent.AGENT_TYPE;
import eu.dnetlib.actionmanager.common.Operation;
import eu.dnetlib.actionmanager.common.Provenance;
import eu.dnetlib.actionmanager.hbase.HBaseActionManagerCore;
import eu.dnetlib.actionmanager.set.ActionManagerSet;
import eu.dnetlib.actionmanager.wf.ActionManagerWorkflowLauncher;
import eu.dnetlib.enabling.inspector.AbstractInspectorController;

@Controller
public class ActionManagerController extends AbstractInspectorController {

	private static final int DEFAULT_PAGE_SIZE = 10;

	@Resource
	private HBaseActionManagerCore core;

	@Resource
	private ActionManagerWorkflowLauncher actionManagerWorkflowLauncher;

	@ModelAttribute(value = "types")
	public ACTION_TYPE[] listActionTypes() {
		return ACTION_TYPE.values();
	}

	@ModelAttribute(value = "provenances")
	public Provenance[] listProvenances() {
		return Provenance.values();
	}

	@ModelAttribute(value = "sets")
	public List<ActionManagerSet> listSets() {
		return core.listAvailableSets();
	}

	@RequestMapping("/inspector/actions/listActions.do")
	public String listActions(
			final ModelMap map,
			@RequestParam(value = "type", required = false) String stype,
			@RequestParam(value = "start", required = false) String start) throws Exception {

		ACTION_TYPE type = (stype == null) ? ACTION_TYPE.all : ACTION_TYPE.valueOf(stype);
		String prefix = (type == ACTION_TYPE.all) ? null : type + "|";
		map.addAttribute("type", type);

		List<Map<String, String>> actions = core.getHbaseClient().retrieveRows(prefix, start, DEFAULT_PAGE_SIZE + 1);

		int size = actions.size();
		if (size > DEFAULT_PAGE_SIZE) {
			map.addAttribute("next", actions.remove(size - 1).get("rowId"));
		}

		map.addAttribute("actions", Lists.transform(actions, new Function<Map<String, String>, Map<String, Object>>() {
			@Override
			public Map<String, Object> apply(Map<String, String> map) {
				return formatAction(map);
			}
		}));

		String template;

		switch (type) {
		case aac:
			template = "listAtomicActions";
			break;
		case pkg:
			template = "listInfoPackages";
			break;
		default:
			template = "listAll";
			break;
		}

		return "inspector/actions/" + template;
	}

	@SuppressWarnings("unchecked")
	private Map<String, Object> formatAction(Map<String, String> map) {
		Map<String, Object> res = Maps.newHashMap();
		for (String key : map.keySet()) {
			if (key.equals("rowId")) {
				res.put("rowId", map.get(key));
			} else if (key.startsWith("date:")) {
				res.put("date", map.get(key));
			} else if (key.startsWith("set:")) {
				res.put("set", map.get(key));
			} else if (key.startsWith("operation:")) {
				res.put("operation", map.get(key));
			} else if (key.endsWith(":" + ACTION_TYPE.pkg)) {
				res.put("infoPackage", map.get(key).replaceAll("<", "&lt;").replaceAll(">", "&gt;"));
			} else if (key.startsWith("rel:")) {
				String nk = map.get(key).trim();
				if (!res.containsKey(nk)) {
					res.put(nk, new ArrayList<String>());
				}
				((List<String>) res.get(nk)).add(key.substring(4));
			} else if (key.startsWith("target:")) {
				res.put(key.replaceAll(":", "_"), map.get(key));
			} else {
				res.put(key, map.get(key));
			}
		}
		return res;
	}

	@RequestMapping("/inspector/actions/registerInfoPackage.do")
	public void registerInfoPackage(
			final ModelMap map,
			@RequestParam(value = "ruleId", required = false) String ruleId,
			@RequestParam(value = "set", required = false) String set,
			@RequestParam(value = "infoPackage", required = false) String infoPackage,
			@RequestParam(value = "provenance", required = false) String provenance,
			@RequestParam(value = "trust", required = false) String trust,
			@RequestParam(value = "nsprefix", required = false) String nsprefix) throws Exception {

		if (ruleId != null && !ruleId.isEmpty() && set != null && !set.isEmpty() && infoPackage != null && !infoPackage.isEmpty() && trust != null
				&& !trust.isEmpty() && provenance != null && !provenance.isEmpty() && nsprefix != null && !nsprefix.isEmpty()
				&& core.getActionFactory().getXslts().containsKey(ruleId)) {

			Agent agent = new Agent();
			agent.setId("inspector");
			agent.setName("inspector");
			agent.setType(AGENT_TYPE.human);

			int n = core.applyInfoPackageAction(ruleId, set, agent, Operation.INSERT, infoPackage, Provenance.convert(provenance), trust, nsprefix);
			map.addAttribute("message", "1 infoPackageAction + " + (n - 1) + " atomicAction(s) registered");
		}

		map.addAttribute("rules", core.getActionFactory().getXslts().keySet());
		map.addAttribute("ruleId", ruleId);
		map.addAttribute("set", set);
		map.addAttribute("infoPackage", infoPackage);
		map.addAttribute("trust", trust);
		map.addAttribute("nsprefix", nsprefix);
	}

	@RequestMapping("/inspector/actions/deleteInfoPackage.do")
	public String deleteInfoPackage(final ModelMap map, @RequestParam(value = "id", required = false) String id) throws Exception {
		core.deleteInfoPackageAction(id);
		return "redirect:listActions.do?type=" + ACTION_TYPE.pkg;
	}

	@RequestMapping("/inspector/actions/bulkdelete.do")
	public void clear(final ModelMap map, @RequestParam(value = "set", required = false) String set) throws Exception {
		map.addAttribute("allSets", ActionManagerWorkflowLauncher.ALL_SETS);
		if (set != null && !set.isEmpty()) {
			actionManagerWorkflowLauncher.executeDelete(set, null, null);
			map.addAttribute("message", "Workflow started !!!");
		}
	}

	@RequestMapping("/inspector/actions/commit.do")
	public void commit(final ModelMap map, @RequestParam(value = "set", required = false) String set) throws Exception {
		map.addAttribute("allSets", ActionManagerWorkflowLauncher.ALL_SETS);
		if (set != null && !set.isEmpty()) {
			actionManagerWorkflowLauncher.executeCommit(set, null, null);
			map.addAttribute("message", "Workflow started !!!");
		}
	}

	@RequestMapping("/inspector/actions/createSet.do")
	public void createSet(
			final ModelMap map,
			@RequestParam(value = "id", required = false) String id,
			@RequestParam(value = "name", required = false) String name) throws Exception {
		if (id != null && name != null && !id.isEmpty() && !name.isEmpty()) {
			core.getInformationServiceClient().registerSetProfile(new ActionManagerSet(id, name));
		}
		map.addAttribute("sets", core.listAvailableSets());
	}
}
