package eu.dnetlib.enabling.inspector;

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

import javax.annotation.Resource;
import javax.xml.transform.dom.DOMResult;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
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.NotificationInvocationLogger;
import eu.dnetlib.enabling.is.sn.resourcestate.ResourceStateSubscription;
import eu.dnetlib.enabling.is.sn.resourcestate.ResourceStateSubscriptionRegistry;
import eu.dnetlib.miscutils.coupling.StaticCondition;

/**
 * Low-level basic interface for managing SN subscriptions.
 *
 * @author marko
 *
 */
@Controller
public class SubscriptionController extends AbstractInspectorController {
	/**
	 * logger.
	 */
	private static final Log log = LogFactory.getLog(SubscriptionController.class); // NOPMD by marko on 11/24/08 5:02 PM

	/**
	 * is sn subscription registries.
	 */
	@Resource(name = "issResourceStateNotificationRegistries")
	private transient List<ResourceStateSubscriptionRegistry> registries;

	/**
	 * allows to control the global enable of issn.
	 */
	@Resource(name = "issnInhibitionCondition")
	private transient StaticCondition inhibitionCondition;

	/**
	 * issn invocation logger.
	 */
	@Resource
	private transient NotificationInvocationLogger invocationLogger;

	/**
	 * show a list of subscriptions.
	 *
	 * @param model
	 *            view parameters
	 * @return view
	 * @throws XPathExpressionException
	 *             could happen
	 */
	@RequestMapping("/inspector/sn.do")
	String listSubscriptions(final Model model) throws XPathExpressionException {

		log.debug("registries: " + registries);

		final List<Map<String, String>> subscriptions = new ArrayList<Map<String, String>>();
		for (ResourceStateSubscriptionRegistry registry : registries) {
			for (ResourceStateSubscription sub : registry.getSubscriptionDao().listSubscriptions()) {
				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());

				final DOMResult result = new DOMResult(); // NOPMD
				sub.getSubscriber().writeTo(result);
				attrs.put("address", XPathFactory.newInstance().newXPath().evaluate("//*[local-name() = 'Address']", result.getNode()));
				subscriptions.add(attrs);
			}
		}

		model.addAttribute("subscriptions", subscriptions);
		model.addAttribute("enabled", !inhibitionCondition.isTrue());
		return "inspector/subscriptions";
	}
	
	/**
	 * show a list of subscriptions.
	 *
	 * @param model
	 *            view parameters
	  @param address
	 *            the address prefix used as filter
	 * @return view
	 * @throws XPathExpressionException
	 *             could happen
	 */
	@RequestMapping("/inspector/snByAddress.do")
	String listSubscriptionsByAddress(final Model model, @RequestParam(value="address", required=false) final String address) throws XPathExpressionException {

		log.debug("registries: " + registries);

		final List<Map<String, String>> subscriptions = new ArrayList<Map<String, String>>();
		log.debug("Address is "+address);
		
			model.addAttribute("address", address);
			
			for (ResourceStateSubscriptionRegistry registry : registries) {
				for (ResourceStateSubscription sub : registry.getSubscriptionDao().listSubscriptions()) {
					final DOMResult result = new DOMResult(); // NOPMD
					sub.getSubscriber().writeTo(result);
					final String addr = XPathFactory.newInstance().newXPath().evaluate("//*[local-name() = 'Address']", result.getNode());
					
					if ( address==null || (addr != null && addr.startsWith(address)) ) {
						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("address", addr);
						subscriptions.add(attrs);
					}
				}
			}
		
		model.addAttribute("subscriptions", subscriptions);
		model.addAttribute("enabled", !inhibitionCondition.isTrue());
		return "inspector/subscriptionsByAddress";
	}
	/**
	 * toggle global notification inhibit flag.
	 *
	 * @return view name (redirect)
	 */
	@RequestMapping("/inspector/toggleNotifications.do")
	String deleteSubscription() {
		inhibitionCondition.setCondition(! inhibitionCondition.isTrue());
		return "redirect:sn.do";
	}

	/**
	 * delete a issn subscription.
	 *
	 * @param subscriptionId
	 *            subscription identifier web parameter
	 * @return view name (redirect)
	 */
	@RequestMapping("/inspector/deleteSubscription.do")
	String deleteSubscription(@RequestParam("id") final String subscriptionId) {
		for (ResourceStateSubscriptionRegistry registry : registries) {
			if (registry.getSubscriptionDao().removeSubscription(subscriptionId))
				break;
		}
		return "redirect:sn.do";
	}
	
	/**
	 * delete issn subscriptions by address prefix.
	 * @param model
	 *            view parameters
	 * @param address
	 *            address prefix 
	 * @return view name (redirect)
	 * @throws XPathExpressionException 
	 */
	@RequestMapping("/inspector/deleteSubscriptionsByAddress.do")
	String deleteSubscriptionsByAddress(final Model model, @RequestParam("address") final String address) throws XPathExpressionException {
		
		final List<String> deleted = new ArrayList<String>();
		
		if (address != null && address.length() > "http://".length()) {
			for (ResourceStateSubscriptionRegistry registry : registries) {
				for (ResourceStateSubscription sub : registry.getSubscriptionDao().listSubscriptions()) {
					final DOMResult result = new DOMResult(); // NOPMD
					sub.getSubscriber().writeTo(result);
					final String addr = XPathFactory.newInstance().newXPath().evaluate("//*[local-name() = 'Address']", result.getNode());
					
					if (addr != null && addr.startsWith(address)) {
						String id = sub.getSubscriptionId();
						registry.getSubscriptionDao().removeSubscription(id);
						deleted.add(id + " ("+ addr +")");
					}
				}
			}
		}
		
		model.addAttribute("deleted", deleted);
		
		return "inspector/deleteSubscriptionsByAddress";
	}
	/**
	 * Show log of notifications.
	 *
	 * @param model mvc model
	 */
	@RequestMapping("/inspector/notificationLog.do")
	void notificationLog(final Model model) {
		model.addAttribute("log", invocationLogger.getEntries());
	}
}
