
package eu.dnetlib.dhp.oa.graph.dump.skgif;

import java.io.Serializable;
import java.util.*;
import java.util.stream.Collectors;

import eu.dnetlib.dhp.oa.graph.dump.skgif.exception.NoAllowedTypeException;
import eu.dnetlib.dhp.oa.graph.dump.skgif.exception.NoTitleFoundException;
import eu.dnetlib.dhp.schema.oaf.*;
import eu.dnetlib.dhp.skgif.model.*;
import eu.dnetlib.dhp.utils.DHPUtils;
import scala.Tuple2;

/**
 * @author miriam.baglioni
 * @Date 01/09/23
 */
public class ResultMapper implements Serializable {

	public static <E extends Result> ResearchProduct map(
		E input)
		throws Exception {

		ResearchProduct out = new ResearchProduct();

		Optional<Qualifier> ort = Optional.ofNullable(input.getResulttype());
		if (ort.isPresent()) {
			try {
				// out.setLocal_identifier(Utils.getIdentifier(Prefixes.RESEARCH_PRODUCT, input.getId()));
				out.setLocal_identifier(input.getId());
				mapPid(out, input);
				mapTitle(out, input);
				mapAbstract(out, input);
				mapType(out, input);
				mapTopic(out, input);
				mapContribution(out, input);

//The manifestation will be included extending the result as well as the relations to funder, organization and other results
				return out;
			} catch (ClassCastException cce) {
				return null;
			}
		}

		return null;

	}

	private static <E extends Result> void mapContribution(ResearchProduct out, E input) {
		if (Optional.ofNullable(input.getAuthor()).isPresent()) {
			int count = 0;
			List<Contribution> contributionList = new ArrayList<>();
			for (Author a : input.getAuthor()) {
				count += 1;
				Contribution contribution = new Contribution();
				Tuple2<String, Boolean> orcid = Utils.getOrcid(a.getPid());
				MinPerson minPerson = new MinPerson();
				minPerson.setFull_name(a.getFullname());
				if (orcid != null) {
					minPerson.setLocal_identifier(Utils.getIdentifier(Prefixes.PERSON, orcid._1() + orcid._2()));
					minPerson.setOrcid(orcid._1());
					contribution.setPerson(minPerson);
				} else {
					if (Optional.ofNullable(a.getRank()).isPresent()) {
						minPerson
							.setLocal_identifier(
								Utils.getIdentifier(Prefixes.TEMPORARY_PERSON, input.getId() + a.getRank()));
						contribution
							.setPerson(minPerson);
					} else {
						minPerson
							.setLocal_identifier(Utils.getIdentifier(Prefixes.TEMPORARY_PERSON, input.getId() + count));
						contribution.setPerson(minPerson);
					}
				}
				if (Optional.ofNullable(a.getRank()).isPresent()) {
					contribution.setRank(a.getRank());
				}

				contributionList.add(contribution);
			}
			out.setContributions(contributionList);
		}

	}

	private static <E extends Result> void mapTopic(ResearchProduct out, E input) {
		if (Optional.ofNullable(input.getSubject()).isPresent()) {
			out
				.setTopics(
					input
						.getSubject()
						.stream()
						.filter(
							s -> s.getQualifier().getClassid().equalsIgnoreCase("fos"))
//							||
//								s.getQualifier().getClassid().equalsIgnoreCase("sdg"))
						.map(s -> {
							ResultTopic topic = new ResultTopic();
							MinTopic minTopic = new MinTopic();
							minTopic
								.setLocal_identifier(
									Utils.getIdentifier(Prefixes.TOPIC, s.getQualifier().getClassid() + s.getValue()));
							minTopic.setValue(s.getValue());
							topic
								.setTopic(minTopic);
							if (Optional.ofNullable(s.getDataInfo()).isPresent()) {
								Provenance provenance = new Provenance();
								try {
									provenance.setTrust(Double.valueOf(s.getDataInfo().getTrust()));
								} catch (NumberFormatException nfe) {

								}
								provenance.setType(s.getDataInfo().getInferenceprovenance());
								topic.setProvenance(provenance);
							}

							return topic;
						})
						.collect(Collectors.toList()));
		}
	}

	private static <E extends Result> void mapType(ResearchProduct out, E input) throws NoAllowedTypeException {
		switch (input.getResulttype().getClassid()) {
			case "publication":
				out.setProduct_type(ResearchTypes.LITERATURE.label);
				break;
			case "dataset":
				out.setProduct_type(ResearchTypes.RESEARCH_DATA.label);
				break;
			case "software":
				out.setProduct_type(ResearchTypes.RESEARCH_SOFTWARE.label);
				break;
			case "other":
				out.setProduct_type(ResearchTypes.OTHER.label);
				break;
			default:
				throw new ClassCastException("Result type not present or not allowed");
		}
	}

	private static void mapPid(ResearchProduct out, Result input) {
		Optional
			.ofNullable(input.getPid())
			.ifPresent(
				value -> out
					.setIdentifiers(
						value
							.stream()
							.map(
								p -> {
									Identifier identifier = new Identifier();
									identifier.setValue(p.getValue());
									identifier.setScheme(p.getQualifier().getClassid());
									return identifier;
								})
							.collect(Collectors.toList())));
	}

	private static void mapTitle(ResearchProduct out, Result input) throws NoTitleFoundException {
		Optional<List<StructuredProperty>> otitle = Optional.ofNullable(input.getTitle());
		if (otitle.isPresent()) {
			List<StructuredProperty> iTitle = otitle
				.get()
				.stream()
				.filter(t -> t.getQualifier().getClassid().equalsIgnoreCase("main title"))
				.collect(Collectors.toList());
			if (!iTitle.isEmpty()) {
				out.setTitles(Collections.singletonMap("none", Arrays.asList(iTitle.get(0).getValue())));
				return;
			}

			iTitle = otitle
				.get()
				.stream()
				.filter(t -> t.getQualifier().getClassid().equalsIgnoreCase("subtitle"))
				.collect(Collectors.toList());
			if (!iTitle.isEmpty()) {
				out.setTitles(Collections.singletonMap("none", Arrays.asList(iTitle.get(0).getValue())));
			}

		}
	}

	private static void mapAbstract(ResearchProduct out, Result input) {
		final List<String> descriptionList = new ArrayList<>();
		Optional
			.ofNullable(input.getDescription())
			.ifPresent(value -> value.forEach(d -> descriptionList.add(d.getValue())));
		out.setAbstracts(Collections.singletonMap("none", descriptionList));
	}
}
