package eu.dnetlib.openaire.context;

import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;

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

import eu.dnetlib.openaire.common.ISClient;
import eu.dnetlib.openaire.exporter.exceptions.ContextException;
import eu.dnetlib.openaire.exporter.model.context.Category;
import eu.dnetlib.openaire.exporter.model.context.CategorySummary;
import eu.dnetlib.openaire.exporter.model.context.Concept;
import eu.dnetlib.openaire.exporter.model.context.ConceptSummary;
import eu.dnetlib.openaire.exporter.model.context.Context;
import eu.dnetlib.openaire.exporter.model.context.ContextSummary;

@Component
@ConditionalOnProperty(value = "openaire.exporter.enable.context", havingValue = "true")
public class ContextApiCore {

	private static final String SEPARATOR = "::";

	@Autowired
	private ISClient isClient;

	public List<ContextSummary> listContexts(final List<String> type) throws ContextException {
		return getContextMap(type).values()
			.stream()
			.map(c -> new ContextSummary()
				.setId(c.getId())
				.setType(c.getType())
				.setLabel(c.getLabel())
				.setStatus(c.getParams().containsKey("status") ? c.getParams().get("status").get(0).getValue() : ""))
			.collect(Collectors.toList());
	}

	public List<CategorySummary> listCategories(final String contextId, final Boolean all) throws ContextException {
		final Stream<Category> categories = getContextMap().get(contextId).getCategories().values().stream();
		return all ? asCategorySummaries(categories) : asCategorySummaries(categories.filter(Category::isClaim));
	}

	private List<CategorySummary> asCategorySummaries(final Stream<Category> categories) {
		return categories
			.map(c -> new CategorySummary()
				.setId(c.getId())
				.setLabel(c.getLabel())
				.setHasConcept(c.hasConcepts()))
			.collect(Collectors.toList());
	}

	public List<ConceptSummary> listConcepts(final String categoryId, final Boolean all) throws ContextException {
		final String contextId = StringUtils.substringBefore(categoryId, SEPARATOR);
		final Stream<Concept> concepts = getContextMap().get(contextId)
			.getCategories()
			.get(categoryId)
			.getConcepts()
			.stream();

		return all ? asConceptSummaries(concepts) : asConceptSummaries(concepts.filter(Concept::isClaim));
	}

	private List<ConceptSummary> asConceptSummaries(final Stream<Concept> concepts) {
		return concepts
			.map(c -> new ConceptSummary()
				.setId(c.getId())
				.setLabel(c.getLabel())
				.setHasSubConcept(c.hasSubConcepts()))
			.collect(Collectors.toList());
	}

	public List<ConceptSummary> listSubConcepts(final String conceptId, final Boolean all) throws ContextException {
		final List<String> ids = Splitter.on(SEPARATOR).splitToList(conceptId);
		if (ids.size() < 3) { throw new ContextException(""); }

		final String contextId = ids.get(0);
		final String categoryId = contextId + SEPARATOR + ids.get(1);

		final Stream<Concept> concepts = getContextMap().get(contextId)
			.getCategories()
			.get(categoryId)
			.getConcepts()
			.stream()
			.filter(c -> conceptId.equals(c.getId()));

		return all ? mapConcepts(concepts.filter(Concept::isClaim).collect(Collectors.toList())) : mapConcepts(concepts.collect(Collectors.toList()));
	}

	private List<ConceptSummary> mapConcepts(final List<Concept> concepts) {
		if (concepts == null || concepts.isEmpty()) { return null; }
		return concepts.stream()
			.map(c -> new ConceptSummary()
				.setId(c.getId())
				.setLabel(c.getLabel())
				.setHasSubConcept(c.hasSubConcepts())
				.setConcept(mapConcepts(c.getConcepts())))
			.collect(Collectors.toList());
	}

	private Map<String, Context> getContextMap() throws ContextException {
		return getContextMap(Lists.newArrayList());
	}

	private Map<String, Context> getContextMap(final List<String> type) throws ContextException {
		try {
			return isClient.getContextMap(type);
		} catch (final IOException e) {
			throw new ContextException(e);
		}
	}

}
