package eu.dnetlib.is.importer;

import java.io.IOException;

import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Node;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import eu.dnetlib.data.is.context.model.Category;
import eu.dnetlib.data.is.context.model.ConceptLevel0;
import eu.dnetlib.data.is.context.model.ConceptLevel1;
import eu.dnetlib.data.is.context.model.ConceptLevel2;
import eu.dnetlib.data.is.context.model.Context;
import eu.dnetlib.data.is.context.model.CtxChildInfo;
import eu.dnetlib.data.is.context.model.CtxInfo;
import eu.dnetlib.data.is.context.model.Parameter;
import eu.dnetlib.data.is.context.repository.CategoryRepository;
import eu.dnetlib.data.is.context.repository.ConceptLevel0Repository;
import eu.dnetlib.data.is.context.repository.ConceptLevel1Repository;
import eu.dnetlib.data.is.context.repository.ConceptLevel2Repository;
import eu.dnetlib.data.is.context.repository.ContextRepository;

@Service
public class ContextImporter {

	@Autowired
	private ContextRepository contextRepository;
	@Autowired
	private CategoryRepository categoryRepository;
	@Autowired
	private ConceptLevel0Repository conceptLevel0Repository;
	@Autowired
	private ConceptLevel1Repository conceptLevel1Repository;
	@Autowired
	private ConceptLevel2Repository conceptLevel2Repository;

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

	public void loadFromOldProfile(final String xml) throws DocumentException, IOException {
		DocumentHelper.parseText(xml)
			.selectNodes("//context")
			.forEach(this::saveContext);
	}

	private void saveContext(final Node node) {
		final Context ctx = new Context();
		setCommonInfo(ctx, null, node);
		ctx.setType(node.valueOf("@type"));
		contextRepository.save(ctx);

		log.info("Saved context: " + ctx.getId());

		node.selectNodes("./category").forEach(n -> saveCategory(n, ctx.getId()));
	}

	private void saveCategory(final Node node, final String ctxId) {
		final Category cat = new Category();
		setCommonInfo(cat, ctxId, node);
		categoryRepository.save(cat);

		log.info("- Saved category: " + cat.getId());

		node.selectNodes("./concept").forEach(n -> saveConceptLvl0(n, cat.getId()));
	}

	private void saveConceptLvl0(final Node node, final String parent) {
		final ConceptLevel0 c0 = new ConceptLevel0();
		setCommonInfo(c0, parent, node);
		conceptLevel0Repository.save(c0);

		log.info("-- Saved concept 0: " + c0.getId());

		node.selectNodes("./concept").forEach(n -> saveConceptLvl1(n, c0.getId()));
	}

	private void saveConceptLvl1(final Node node, final String parent) {
		final ConceptLevel1 c1 = new ConceptLevel1();
		setCommonInfo(c1, parent, node);
		conceptLevel1Repository.save(c1);

		log.info("--- Saved concept 1: " + c1.getId());

		node.selectNodes("./concept").forEach(n -> saveConceptLvl2(n, c1.getId()));
	}

	private void saveConceptLvl2(final Node node, final String parent) {
		final ConceptLevel2 c2 = new ConceptLevel2();
		setCommonInfo(c2, parent, node);
		conceptLevel2Repository.save(c2);

		log.info("---- Saved concept 2: " + c2.getId());
	}

	private void setCommonInfo(final CtxInfo o, final String parent, final Node n) {
		o.setId(n.valueOf("@id"));
		o.setLabel(n.valueOf("@label"));
		if (o instanceof CtxChildInfo) {
			((CtxChildInfo) o).setParent(parent);
			((CtxChildInfo) o).setClaim(BooleanUtils.toBoolean(n.valueOf("@claim")));
		}
		o.setParameters(n.selectNodes("./param")
			.stream()
			.map(np -> new Parameter(np.valueOf("@name"), np.getText()))
			.sorted()
			.toArray(Parameter[]::new));

	}
}
