package eu.dnetlib.vocabularies.publisher.controller;

import java.util.List;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

import eu.dnetlib.common.rmi.DNetRestDocumentation;
import eu.dnetlib.vocabularies.publisher.VocabularyNotFoundException;
import eu.dnetlib.vocabularies.publisher.VocabularyRetriever;
import eu.dnetlib.vocabularies.publisher.model.Vocabulary;
import eu.dnetlib.vocabularies.publisher.model.VocabularyTerm;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.DocumentException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.HandlerMapping;

@Controller
@DNetRestDocumentation
@CrossOrigin(origins = { "*" })
public class VocabularyPublisherController {

	private static final Log log = LogFactory.getLog(VocabularyPublisherController.class);

	@Resource
	private VocabularyRetriever vocabularyRetriever;
	@Value("${vocabulary.publisher.title}")
	private String title;
	@Value("${vocabulary.publisher.baseURL}")
	private String baseURL;
	@Value("${container.context}")
	private String context;

	@RequestMapping(value = "/vocabularies", method = RequestMethod.GET, produces = { "application/json" })
	public @ResponseBody
	List<Vocabulary> listVocabularies() {
		log.debug("listVocabularies()");
		return vocabularyRetriever.listVocabularies();
	}

	@RequestMapping(value = "/vocabularies/{code}", method = RequestMethod.GET, produces = { "application/json" })
	public @ResponseBody
	Vocabulary getVocabulary(@PathVariable("code") final String code) throws VocabularyNotFoundException {
		log.debug("getVocabulary with code = " + code);
		return this.vocabularyRetriever.getVocabularyByCode(code);
	}

	@RequestMapping(value = "/vocabularies/{code}/**", method = RequestMethod.GET, produces = { "application/json" })
	public @ResponseBody
	VocabularyTerm getTermSynonyms(
			@PathVariable("code") final String code,
			final HttpServletRequest request) throws VocabularyNotFoundException {
		final String term = extractTerm(request);
		log.debug(String.format("getVocabulary with code = %s, term = %s", code, term));
		return this.vocabularyRetriever.getTermSynonyms(code, term);
	}


	private String extractTerm(HttpServletRequest request) {

		String path = (String) request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE); // /elements/CATEGORY1/CATEGORY1_1/ID
		String bestMatchPattern = (String) request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE); // /elements/**

		return StringUtils.substringBeforeLast(new AntPathMatcher().extractPathWithinPattern(bestMatchPattern, path), ".json"); // CATEGORY1/CATEGORY1_1/ID
	}

	// View-based methods for html responses
	@RequestMapping(value = "/vocabularies", method = RequestMethod.GET, produces = { "text/html" })
	public void listVocabularies(final ModelMap map) {
		map.addAttribute("vocabularies", listVocabularies());
		map.addAttribute("title", title);
		map.addAttribute("baseURL", baseURL);
		map.addAttribute("context", context);
	}

	@RequestMapping(value = "/vocabularies/{code}", method = RequestMethod.GET, produces = { "text/html" })
	public String getVocabulary(
			@PathVariable("code") final String code,
			final ModelMap map) throws VocabularyNotFoundException {

		map.addAttribute("vocabulary", getVocabulary(code));
		map.addAttribute("baseURL", baseURL);
		map.addAttribute("context", context);
		return "displayVocabulary";
	}

	@RequestMapping(value = "/vocabularies/{code}/**", method = RequestMethod.GET, produces = { "text/html" })
	public String getTermSynonyms(
			@PathVariable("code") final String code,
			final HttpServletRequest request,
			final ModelMap map) throws VocabularyNotFoundException {
		map.addAttribute("vocabulary", getVocabulary(code));
		map.addAttribute("term", getTermSynonyms(code, request));
		map.addAttribute("baseURL", baseURL);
		map.addAttribute("context", context);
		return "displaySynonyms";
	}

}
