package eu.dnetlib.is.info;

import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.maven.model.Model;
import org.apache.maven.model.Parent;
import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.CompositePropertySource;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.EnumerablePropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.ResourcePatternUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import eu.dnetlib.common.controller.AbstractDnetController;

@RestController
@RequestMapping("/ajax/info")
public class InfoAjaxController extends AbstractDnetController {

	@Autowired
	private ConfigurableEnvironment configurableEnvironment;

	@Autowired
	private ResourceLoader resourceLoader;

	private final RuntimeMXBean mxbean = ManagementFactory.getRuntimeMXBean();

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

	@GetMapping("/")
	public List<InfoSection<?>> info() throws Exception {
		final List<InfoSection<?>> res = new ArrayList<>();
		res.add(jvm());
		res.add(args());
		res.addAll(props());
		res.add(modules());
		return res;
	}

	private InfoSection<KeyValue> jvm() {
		final InfoSection<KeyValue> jvm = new InfoSection<>("JVM");
		jvm.getData().add(new KeyValue("JVM Name", mxbean.getVmName()));
		jvm.getData().add(new KeyValue("JVM Vendor", mxbean.getVmVendor()));
		jvm.getData().add(new KeyValue("JVM Version", mxbean.getVmVersion()));
		jvm.getData().add(new KeyValue("JVM Spec Name", mxbean.getSpecName()));
		jvm.getData().add(new KeyValue("JVM Spec Vendor", mxbean.getSpecVendor()));
		jvm.getData().add(new KeyValue("JVM Spec Version", mxbean.getSpecVersion()));
		jvm.getData().add(new KeyValue("Running JVM Name", mxbean.getName()));
		jvm.getData().add(new KeyValue("Management Spec Version", mxbean.getManagementSpecVersion()));
		return jvm;
	}

	private InfoSection<KeyValue> args() {
		final InfoSection<KeyValue> libs = new InfoSection<>("Arguments");
		libs.getData().add(new KeyValue("Input arguments", StringUtils.join(mxbean.getInputArguments(), " ")));
		return libs;
	}

	private List<InfoSection<KeyValue>> props() {
		final List<InfoSection<KeyValue>> res = new ArrayList<>();

		configurableEnvironment.getPropertySources().forEach(ps -> {
			final InfoSection<KeyValue> section = new InfoSection<>("Properties: " + ps.getName());
			addAllProperties(section, ps);
			res.add(section);
		});

		return res;
	}

	private void addAllProperties(final InfoSection<KeyValue> res, final PropertySource<?> ps) {
		if (ps instanceof CompositePropertySource) {
			final CompositePropertySource cps = (CompositePropertySource) ps;
			cps.getPropertySources().forEach(x -> addAllProperties(res, x));
		} else if (ps instanceof EnumerablePropertySource<?>) {
			final EnumerablePropertySource<?> eps = (EnumerablePropertySource<?>) ps;
			Arrays.asList(eps.getPropertyNames()).forEach(k -> res.getData().add(new KeyValue(k, eps.getProperty(k))));
		} else {}
	}

	public InfoSection<JavaModule> modules() throws IOException {
		final Map<String, Map<String, JavaModule>> modules = new LinkedHashMap<>();

		final MavenXpp3Reader reader = new MavenXpp3Reader();
		for (final Resource res : ResourcePatternUtils.getResourcePatternResolver(resourceLoader).getResources("classpath*:/META-INF/**/pom.xml")) {
			try {
				final Model model = reader.read(res.getInputStream());

				final String name = model.getArtifactId();

				String groupId = model.getGroupId();
				for (Parent parent = model.getParent(); groupId == null && model.getParent() != null; parent = model.getParent()) {
					groupId = parent.getGroupId();
				}

				String version = model.getVersion();
				for (Parent parent = model.getParent(); version == null && model.getParent() != null; parent = model.getParent()) {
					version = parent.getVersion();
				}

				if (!modules.containsKey(groupId)) {
					modules.put(groupId, new HashMap<String, JavaModule>());
				}
				if (!modules.get(groupId).containsKey(name)) {
					modules.get(groupId).put(name, new JavaModule(groupId, name));
				}
				modules.get(groupId).get(name).addFileAndVersion(res.getURI().toString(), version);
			} catch (final Exception e) {
				log.error("Error evaluating pom: " + res.getURI());
				log.debug("-- ERROR --", e);
			}
		}

		final InfoSection<JavaModule> res = new InfoSection<>("Modules");

		for (final Entry<String, Map<String, JavaModule>> e : modules.entrySet()) {
			for (final Entry<String, JavaModule> e1 : e.getValue().entrySet()) {
				res.getData().add(e1.getValue());
			}
		}

		Collections.sort(res.getData());

		return res;
	}

}
