package eu.eudat.models.rda.mapper;

import eu.eudat.data.entities.*;
import eu.eudat.logic.services.ApiContext;
import eu.eudat.logic.utilities.helpers.StreamDistinctBy;
import eu.eudat.models.rda.Cost;
import eu.eudat.models.rda.Dmp;
import eu.eudat.models.rda.DmpId;
import net.minidev.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.transaction.Transactional;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

@Component
public class DmpRDAMapper {

	private DatasetRDAMapper datasetRDAMapper;
	private ApiContext apiContext;


	@Autowired
	public DmpRDAMapper(DatasetRDAMapper datasetRDAMapper, ApiContext apiContext) throws IOException {
		this.datasetRDAMapper = datasetRDAMapper;
		this.apiContext = apiContext;

	}

	@Transactional
	public Dmp toRDA(DMP dmp) {
		if (dmp.getDataset() == null || dmp.getDataset().isEmpty()) {
			throw new IllegalArgumentException("DMP has no Datasets");
		}
		Map<String, Object> extraProperties;
		if (dmp.getExtraProperties() == null) {
			throw new IllegalArgumentException("DMP is missing language and contact properties");
		} else {
			extraProperties = new org.json.JSONObject(dmp.getExtraProperties()).toMap();
			/*if (extraProperties.get("language") == null) {
				throw new IllegalArgumentException("DMP must have it's language property defined");
			}*/
			if (extraProperties.get("contact") == null) {
				throw new IllegalArgumentException("DMP must have it's contact property defined");
			}
		}
		Dmp rda = new Dmp();
		if (dmp.getDoi() != null && !dmp.getDoi().isEmpty()) {
			rda.setDmpId(DmpIdRDAMapper.toRDA(dmp.getDoi()));
		} else {
			rda.setDmpId(DmpIdRDAMapper.toRDA(dmp.getId()));
		}
		if (dmp.getCreated() == null) {
			throw new IllegalArgumentException("DMP Created is missing");
		}
		if (dmp.getModified() == null) {
			throw new IllegalArgumentException("DMP Modified is missing");
		}
		if (dmp.getLabel() == null) {
			throw new IllegalArgumentException("DMP Label is missing");
		}
		rda.setCreated(dmp.getCreated());
		rda.setDescription(dmp.getDescription());
		rda.setModified(dmp.getModified());
		rda.setTitle(dmp.getLabel());

		if (!extraProperties.isEmpty()) {
			if (extraProperties.get("ethicalIssues") != null) {
				rda.setEthicalIssuesExist(Dmp.EthicalIssuesExist.fromValue(extraProperties.get("ethicalIssues").toString()));
			} else {
				rda.setEthicalIssuesExist(Dmp.EthicalIssuesExist.UNKNOWN);
			}
			rda.setLanguage(LanguageRDAMapper.mapLanguageIsoToRDAIso(extraProperties.get("language") != null ? extraProperties.get("language").toString() : "en"));
			if (extraProperties.get("costs") != null) {
				rda.setCost(new ArrayList<>());
				((List) extraProperties.get("costs")).forEach(costl -> {
					rda.getCost().add(CostRDAMapper.toRDA((Map)costl));
				});
			}
			UserInfo contact = apiContext.getOperationsContext().getDatabaseRepository().getUserInfoDao().find(UUID.fromString((String) extraProperties.get("contact")));
			rda.setContact(ContactRDAMapper.toRDA(contact));
		}

		/*UserInfo creator;
		if (dmp.getCreator() != null) {
			creator = dmp.getCreator();
		} else {
			creator = dmp.getUsers().stream().filter(userDMP -> userDMP.getRole().equals(UserDMP.UserDMPRoles.OWNER.getValue())).map(UserDMP::getUser).findFirst().orElse(new UserInfo());
		}
		rda.setContact(ContactRDAMapper.toRDA(creator));*/
		rda.setContributor(new ArrayList<>());
		if (dmp.getResearchers() != null && !dmp.getResearchers().isEmpty()) {
			rda.getContributor().addAll(dmp.getResearchers().stream().map(ContributorRDAMapper::toRDA).collect(Collectors.toList()));
		}
//		rda.getContributor().addAll(dmp.getUsers().stream().map(ContributorRDAMapper::toRDA).collect(Collectors.toList()));
		rda.setDataset(dmp.getDataset().stream().filter(dataset -> dataset.getStatus() != eu.eudat.elastic.entities.Dmp.DMPStatus.DELETED.getValue()).map(dataset -> datasetRDAMapper.toRDA(dataset, rda)).collect(Collectors.toList()));
		rda.setProject(Collections.singletonList(ProjectRDAMapper.toRDA(dmp.getProject(), dmp.getGrant())));
		rda.setAdditionalProperty("templates", dmp.getAssociatedDmps().stream().map(datasetProfile -> datasetProfile.getId().toString()).toArray());
		return rda;
	}

	public DMP toEntity(Dmp rda, String[] profiles) {
		DMP entity = new DMP();
		entity.setLabel(rda.getTitle());
		if (rda.getDmpId().getType() == DmpId.Type.DOI) {
			entity.setDoi(rda.getDmpId().getIdentifier());
		}
		if (((List<String>) rda.getAdditionalProperties().get("templates")) != null && !((List<String>) rda.getAdditionalProperties().get("templates")).isEmpty()) {
			entity.setAssociatedDmps(((List<String>) rda.getAdditionalProperties().get("templates")).stream().map(this::getProfile).filter(Objects::nonNull).collect(Collectors.toSet()));
		}
		if (entity.getAssociatedDmps() == null) {
			entity.setAssociatedDmps(new HashSet<>());
		}
		if (profiles != null) {
			for (String profile : profiles) {
				DatasetProfile exProfile = apiContext.getOperationsContext().getDatabaseRepository().getDatasetProfileDao().find(UUID.fromString(profile));
				entity.getAssociatedDmps().add(exProfile);
			}
		}
		if (rda.getContributor() != null && !rda.getContributor().isEmpty() && rda.getContributor().get(0).getContributorId() != null) {
			entity.setResearchers(rda.getContributor().stream().map(ContributorRDAMapper::toEntity).filter(StreamDistinctBy.distinctByKey(Researcher::getReference)).collect(Collectors.toSet()));
		}
		entity.setCreated(rda.getCreated());
		entity.setModified(rda.getModified());
		entity.setDescription(rda.getDescription());
		DatasetProfile defaultProfile = ((DatasetProfile)entity.getAssociatedDmps().toArray()[0]);
		entity.setDataset(rda.getDataset().stream().map(rda1 -> datasetRDAMapper.toEntity(rda1, defaultProfile)).collect(Collectors.toSet()));
		if (rda.getProject().size() > 0) {
			Map<String, Object> result = ProjectRDAMapper.toEntity(rda.getProject().get(0), apiContext);
			entity.setProject((Project) result.get("project"));
			result.entrySet().stream().filter(entry -> entry.getKey().startsWith("grant")).forEach(entry -> entity.setGrant((Grant) entry.getValue()));
		}

		Map<String, Object> extraProperties = new HashMap<>();
		extraProperties.put("language", LanguageRDAMapper.mapRDAIsoToLanguageIso(rda.getLanguage()));

		entity.setExtraProperties(JSONObject.toJSONString(extraProperties));

		return entity;
	}

	private DatasetProfile getProfile(String id) {
		return  apiContext.getOperationsContext().getDatabaseRepository().getDatasetProfileDao().asQueryable().where(((builder, root) -> builder.equal(root.get("id"), UUID.fromString(id)))).getSingleOrDefault();
	}
}
