package eu.eudat.logic.managers;

import com.fasterxml.jackson.databind.ObjectMapper;
import eu.eudat.configurations.dynamicgrant.DynamicGrantConfiguration;
import eu.eudat.configurations.dynamicgrant.entities.Property;
import eu.eudat.data.dao.criteria.*;
import eu.eudat.data.dao.entities.*;
import eu.eudat.data.entities.Organisation;
import eu.eudat.data.entities.Researcher;
import eu.eudat.data.entities.*;
import eu.eudat.data.enumeration.notification.ActiveStatus;
import eu.eudat.data.enumeration.notification.ContactType;
import eu.eudat.data.enumeration.notification.NotificationType;
import eu.eudat.data.enumeration.notification.NotifyState;
import eu.eudat.data.query.items.table.dataset.DatasetTableRequest;
import eu.eudat.data.query.items.table.datasetprofile.DatasetProfileTableRequestItem;
import eu.eudat.data.query.items.table.dmp.DataManagementPlanTableRequest;
import eu.eudat.depositinterface.models.DMPDepositModel;
import eu.eudat.depositinterface.repository.RepositoryDeposit;
import eu.eudat.elastic.criteria.DmpCriteria;
import eu.eudat.elastic.entities.Collaborator;
import eu.eudat.elastic.entities.Dmp;
import eu.eudat.elastic.entities.Organization;
import eu.eudat.elastic.entities.Tag;
import eu.eudat.exceptions.datamanagementplan.DMPNewVersionException;
import eu.eudat.exceptions.datamanagementplan.DMPWithDatasetsDeleteException;
import eu.eudat.exceptions.security.ForbiddenException;
import eu.eudat.exceptions.security.NonValidTokenException;
import eu.eudat.exceptions.security.UnauthorisedException;
import eu.eudat.logic.builders.entity.UserInfoBuilder;
import eu.eudat.logic.mapper.elastic.DmpMapper;
import eu.eudat.logic.mapper.elastic.criteria.DmpCriteriaMapper;
import eu.eudat.logic.proxy.config.configloaders.ConfigLoader;
import eu.eudat.logic.security.repositorydeposit.mapper.DMPToDepositMapper;
import eu.eudat.logic.services.ApiContext;
import eu.eudat.logic.services.forms.VisibilityRuleService;
import eu.eudat.logic.services.forms.VisibilityRuleServiceImpl;
import eu.eudat.logic.services.operations.DatabaseRepository;
import eu.eudat.logic.utilities.builders.XmlBuilder;
import eu.eudat.logic.utilities.documents.helpers.FileEnvelope;
import eu.eudat.logic.utilities.documents.pdf.PDFUtils;
import eu.eudat.logic.utilities.documents.types.ParagraphStyle;
import eu.eudat.logic.utilities.documents.word.WordBuilder;
import eu.eudat.logic.utilities.documents.xml.ExportXmlBuilder;
import eu.eudat.models.HintedModelFactory;
import eu.eudat.models.data.dataset.DatasetOverviewModel;
import eu.eudat.models.data.datasetprofile.DatasetProfileListingModel;
import eu.eudat.models.data.datasetwizard.DatasetWizardModel;
import eu.eudat.models.data.datasetwizard.DatasetsToBeFinalized;
import eu.eudat.models.data.dmp.*;
import eu.eudat.models.data.doi.DepositRequest;
import eu.eudat.models.data.doi.Doi;
import eu.eudat.models.data.dynamicfields.DynamicFieldWithValue;
import eu.eudat.models.data.entities.xmlmodels.dmpprofiledefinition.DataManagementPlanProfile;
import eu.eudat.models.data.entities.xmlmodels.dmpprofiledefinition.Field;
import eu.eudat.models.data.funder.FunderDMPEditorModel;
import eu.eudat.models.data.grant.GrantDMPEditorModel;
import eu.eudat.models.data.helpermodels.Tuple;
import eu.eudat.models.data.helpers.common.DataTableData;
import eu.eudat.models.data.listingmodels.*;
import eu.eudat.models.data.project.ProjectDMPEditorModel;
import eu.eudat.models.data.security.Principal;
import eu.eudat.models.data.user.composite.PagedDatasetProfile;
import eu.eudat.models.data.userinfo.UserListingModel;
import eu.eudat.queryable.QueryableList;
import eu.eudat.types.Authorities;
import eu.eudat.types.MetricNames;
import org.apache.commons.io.IOUtils;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.http.*;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import javax.transaction.Transactional;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import java.io.*;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Component
public class DataManagementPlanManager {
    private static final Logger logger = LoggerFactory.getLogger(DataManagementPlanManager.class);
    private final ObjectMapper objectMapper;

    private final Map<NotificationType, String> notificationPaths = Stream.of(new Object[][] {
            {NotificationType.DMP_MODIFIED, "/plans/edit"},
            {NotificationType.DMP_PUBLISH, "/plans/publicEdit"},
            {NotificationType.DMP_FINALISED, "/plans/edit"},
            {NotificationType.DMP_MODIFIED_FINALISED, "/plans/edit"}
    }).collect(Collectors.toMap(data -> (NotificationType) data[0], data -> (String) data[1]));

    private ApiContext apiContext;
    private DatasetManager datasetManager;
    private DatabaseRepository databaseRepository;
    private Environment environment;
    private RDAManager rdaManager;
    private UserManager userManager;
    private final MetricsManager metricsManager;
    private final ConfigLoader configLoader;
    private List<RepositoryDeposit> repositoriesDeposit;

    @Autowired
    public DataManagementPlanManager(ApiContext apiContext, DatasetManager datasetManager, Environment environment, RDAManager rdaManager, UserManager userManager,
                                     MetricsManager metricsManager, ConfigLoader configLoader, List<RepositoryDeposit> repositoriesDeposit) {
        this.apiContext = apiContext;
        this.datasetManager = datasetManager;
        this.databaseRepository = apiContext.getOperationsContext().getDatabaseRepository();
        this.environment = environment;
        this.rdaManager = rdaManager;
        this.userManager = userManager;
        this.metricsManager = metricsManager;
        this.configLoader = configLoader;
        this.objectMapper = new ObjectMapper();
        this.repositoriesDeposit = repositoriesDeposit;
    }

    /*
    * Data Retrieval
    * */

    public DataTableData<DataManagementPlanListingModel> getPaged(DataManagementPlanTableRequest dataManagementPlanTableRequest, Principal principal, String fieldsGroup) throws Exception {
        UUID principalID = principal.getId();
        List<Dmp> dmps = null;
        QueryableList<DMP> items = null;
        QueryableList<DMP> authItems = null;
        Long totalData = 0L;
        if (apiContext.getOperationsContext().getElasticRepository().getDmpRepository() != null) {
            try {
                DmpCriteria criteria = DmpCriteriaMapper.toElasticCriteria(dataManagementPlanTableRequest.getCriteria(), principalID);
                criteria.setOffset(dataManagementPlanTableRequest.getOffset());
                criteria.setSize(dataManagementPlanTableRequest.getLength());
                criteria.setSortCriteria(DmpCriteriaMapper.toElasticSorting(dataManagementPlanTableRequest.getOrderings()));

                dmps = apiContext.getOperationsContext().getElasticRepository().getDmpRepository().query(criteria);
                if (dmps != null && !dmps.isEmpty()) {
                    List<Dmp> finalDmps = dmps;
                    items = apiContext.getOperationsContext().getDatabaseRepository().getDmpDao().asQueryable().where((builder, root) -> root.get("id").in(finalDmps.stream().map(Dmp::getId).collect(Collectors.toList())));
                    PaginationManager.applyOrder(items, dataManagementPlanTableRequest);
                    totalData = apiContext.getOperationsContext().getElasticRepository().getDmpRepository().count(criteria);
                }
            } catch (Exception ex) {
                logger.warn(ex.getMessage(), ex);
                items = null;
            }
        }

        if (items == null) {
            items = apiContext.getOperationsContext().getDatabaseRepository().getDmpDao().getWithCriteria(dataManagementPlanTableRequest.getCriteria());
        }
            List<Integer> roles = new LinkedList<>();
            if (!dataManagementPlanTableRequest.getCriteria().isOnlyPublic()) {
                if (dataManagementPlanTableRequest.getCriteria().getRole() != null)
                    roles.add(dataManagementPlanTableRequest.getCriteria().getRole());
                authItems = apiContext.getOperationsContext().getDatabaseRepository().getDmpDao().getAuthenticated(items, principalID, roles);
            } else {
                authItems = items;
            }
        if (dmps == null) {
            totalData = authItems.count();
            items = PaginationManager.applyPaging(authItems, dataManagementPlanTableRequest);
        } else {
            items = authItems;
        }



        DataTableData<DataManagementPlanListingModel> dataTable = new DataTableData<>();

        if (fieldsGroup.equals("listing")) {
            if (!dataManagementPlanTableRequest.getCriteria().isOnlyPublic()) {
                List<DMP> dmps1 = items.withHint(HintedModelFactory.getHint(DataManagementPlanListingModel.class))
                                        .distinct().toList();
                dataTable.setData(dmps1.stream().map(dmp -> {
                    DatasetCriteria datasetCriteria = new DatasetCriteria();
                    datasetCriteria.setDmpIds(Collections.singletonList(dmp.getId()));
                    datasetCriteria.setAllVersions(dataManagementPlanTableRequest.getCriteria().getAllVersions());
                    datasetCriteria.setIsPublic(dataManagementPlanTableRequest.getCriteria().getIsPublic());
                    datasetCriteria.setGroupIds(Collections.singletonList(dmp.getGroupId()));
                    dmp.setDataset(retrieveRelevantDatasets(datasetCriteria, principalID));


                    return new DataManagementPlanListingModel().fromDataModelDatasets(dmp);
                }).collect(Collectors.toList()));
                /*.selectAsync(item -> {
                                            item.setDataset(
                                                    item.getDataset().stream()
                                                        .filter(dataset -> !dataset.getStatus().equals(Dataset.Status.DELETED.getValue()) && !dataset.getStatus().equals(Dataset.Status.CANCELED.getValue())).collect(Collectors.toList()).stream()
                                                        *//*.filter(dataset ->  dataset.getDmp().getUsers().stream()
                                                                          .filter(x -> x.getUser().getId().equals(principalID))
                                                                          .collect(Collectors.toList()).size() > 0)*//*
                                                        .collect(Collectors.toSet()));
                                            return new DataManagementPlanListingModel().fromDataModelDatasets(item);
                                        })
                                        .whenComplete((resultList, throwable) -> dataTable.setData(resultList));*/
            } else {
                List<DMP> dmps1 = items.withHint(HintedModelFactory.getHint(DataManagementPlanListingModel.class))
                                        .distinct().toList();
                dataTable.setData(dmps1.stream().map(dmp -> {
                    DatasetCriteria datasetCriteria = new DatasetCriteria();
                    datasetCriteria.setDmpIds(Collections.singletonList(dmp.getId()));
                    datasetCriteria.setIsPublic(true);
                    datasetCriteria.setAllVersions(dataManagementPlanTableRequest.getCriteria().getAllVersions());
                    datasetCriteria.setGroupIds(Collections.singletonList(dmp.getGroupId()));
                    dmp.setDataset(retrieveRelevantDatasets(datasetCriteria));


                    return new DataManagementPlanListingModel().fromDataModelDatasets(dmp);
                }).collect(Collectors.toList()));
                                        /*.selectAsync(item -> {
                                            item.setDataset(
                                                    item.getDataset().stream()
                                                        .filter(dataset -> dataset.getStatus().equals(Dataset.Status.FINALISED.getValue())).collect(Collectors.toSet()));
                                            return new DataManagementPlanListingModel().fromDataModelDatasets(item);
                                        })
                                        .whenComplete((resultList, throwable) -> dataTable.setData(resultList));*/
            }
        } else if (fieldsGroup.equals("autocomplete")) {
            dataTable.setData(items
                    .distinct()
                    .select(item -> new DataManagementPlanListingModel().fromDataModelAutoComplete(item)));
        } else {
            dataTable.setData(items
                    .distinct()
                    .select(item -> new DataManagementPlanListingModel().fromDataModelAssociatedProfiles(item)));
        }

        //CompletableFuture countFuture = authItems.distinct().countAsync().whenComplete((count, throwable) -> dataTable.setTotalCount(count));
        dataTable.setTotalCount(totalData);
        //CompletableFuture.allOf(itemsFuture).join();
        return dataTable;
    }

    private Set<Dataset> retrieveRelevantDatasets(DatasetCriteria datasetCriteria) {
        return retrieveRelevantDatasets(datasetCriteria, null);
    }

    private Set<Dataset> retrieveRelevantDatasets (DatasetCriteria datasetCriteria, UUID principal) {
        QueryableList<Dataset> datasetItems = apiContext.getOperationsContext().getDatabaseRepository().getDatasetDao().getWithCriteria(datasetCriteria)
                .orderBy((builder, root) -> builder.desc(root.get("modified")));
        if (principal != null) {
            UserInfo userInfo = apiContext.getOperationsContext().getDatabaseRepository().getUserInfoDao().find(principal);
            List<Integer> roles = new ArrayList<>();
            roles.add(0);
            roles.add(1);
            datasetItems = apiContext.getOperationsContext().getDatabaseRepository().getDatasetDao().getAuthenticated(datasetItems, userInfo, roles);
        }
        Long maxDatasets = datasetItems.distinct().count();
        DatasetTableRequest datasetTableRequest = new DatasetTableRequest();
        datasetTableRequest.setOffset(0);
        datasetTableRequest.setLength(3);
        Set<Dataset> datasetsSet = new LinkedHashSet<>();
        try {
            datasetItems = PaginationManager.applyPaging(datasetItems, datasetTableRequest);
            List<Dataset> datasets = datasetItems.distinct().toList();
            datasetsSet.addAll(datasets);
            for (int i = 0; i < maxDatasets - datasets.size(); i++) {
                Dataset fakedataset = new Dataset();
                fakedataset.setId(UUID.randomUUID());
                datasetsSet.add(fakedataset);
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }

        return datasetsSet;
    }

    public eu.eudat.models.data.dmp.DataManagementPlan getSingle(String id, Principal principal, boolean isPublic, boolean includeDatasets) throws Exception {
        eu.eudat.models.data.dmp.DataManagementPlan dataManagementPlan = new eu.eudat.models.data.dmp.DataManagementPlan();
        DMP dataManagementPlanEntity = databaseRepository.getDmpDao().find(UUID.fromString(id));
        if (!isPublic && principal == null) {
            throw new UnauthorisedException();
        } else if (!isPublic && (dataManagementPlanEntity.getUsers().stream().noneMatch(userInfo -> userInfo.getUser().getId() == principal.getId()))) {
            if (!dataManagementPlanEntity.isPublic()) {
                throw new UnauthorisedException();
            }
        } else if (isPublic && !dataManagementPlanEntity.isPublic()) {
            throw new ForbiddenException("Selected DMP is not public");
        }
        if (includeDatasets) {
            dataManagementPlan.fromDataModel(dataManagementPlanEntity);
            dataManagementPlan.getDatasets().forEach(datasetWizardModel -> datasetWizardModel.setDescription(null));
            dataManagementPlan.setDatasets(dataManagementPlan.getDatasets().stream().sorted(Comparator.comparing(DatasetWizardModel::getCreated).reversed()).collect(Collectors.toList()));
            //List<Dataset> datasetEnities = new ArrayList<>(dataManagementPlanEntity.getDataset());
        /*for (int i = 0; i < datasetEnities.size(); i++) {
            for (int j = i; j < dataManagementPlan.getDatasets().size(); j++) {
                if (dataManagementPlan.getDatasets().get(j).getId().equals(datasetEnities.get(i).getId())) {
                    dataManagementPlan.getDatasets().get(j).setDatasetProfileDefinition(datasetManager.getPagedProfile(dataManagementPlan.getDatasets().get(j), datasetEnities.get(i)));
                    break;
                }
            }
        }*/
            /*datasetEnities.stream()
                    .filter(dataset -> !dataset.getStatus().equals(Dataset.Status.DELETED) && !dataset.getStatus().equals(Dataset.Status.CANCELED))
                    .forEach(dataset -> {
                        dataManagementPlan.getDatasets().stream().filter(datasetWizardModel -> datasetWizardModel.getId().equals(dataset.getId())).forEach(datasetWizardModel -> {
                            DatasetWizardModel wizardModel = datasetManager.getSingle(datasetWizardModel.getId().toString(), principal);
                            datasetWizardModel.setDatasetProfileDefinition(wizardModel.getDatasetProfileDefinition());
                            datasetWizardModel.setTags(wizardModel.getTags());
                        });
                    });*/
            if (isPublic) {
                dataManagementPlan.setDatasets(dataManagementPlan.getDatasets().stream().filter(dataset -> dataset.getStatus() == Dataset.Status.FINALISED.getValue()).collect(Collectors.toList()));
            }
        } else {
            dataManagementPlan.fromDataModelNoDatasets(dataManagementPlanEntity);
        }
        Map dmpProperties = dataManagementPlanEntity.getDmpProperties() != null ? new org.json.JSONObject(dataManagementPlanEntity.getDmpProperties()).toMap() : null;

        if (dmpProperties != null && dataManagementPlan.getDynamicFields() != null)
            dataManagementPlan.getDynamicFields().forEach(item -> {
                Map<String, String> properties = (Map<String, String>) dmpProperties.get(item.getId());
                if (properties != null)
                    item.setValue(new Tuple<>(properties.get("id"), properties.get("label")));
            });
        return dataManagementPlan;
    }

    public DataManagementPlanOverviewModel getOverviewSingle(String id, Principal principal, boolean isPublic) throws Exception {
        DMP dataManagementPlanEntity = databaseRepository.getDmpDao().find(UUID.fromString(id));
        if (dataManagementPlanEntity.getStatus() == DMP.DMPStatus.DELETED.getValue()) {
            throw new Exception("DMP is deleted.");
        }
        if (!isPublic && principal == null) {
            throw new UnauthorisedException();
        } else
        if (!isPublic && dataManagementPlanEntity.getUsers()
                                    .stream().noneMatch(userInfo -> userInfo.getUser().getId() == principal.getId())) {
            throw new UnauthorisedException();
        } else if (isPublic && !dataManagementPlanEntity.isPublic()) {
            throw new ForbiddenException("Selected DMP is not public");
        }
        DataManagementPlanOverviewModel datamanagementPlan = new DataManagementPlanOverviewModel();
        datamanagementPlan.fromDataModelDatasets(dataManagementPlanEntity);
        datamanagementPlan.setDatasets(datamanagementPlan.getDatasets().stream().sorted(Comparator.comparing(DatasetOverviewModel::getCreated).reversed()).collect(Collectors.toList()));
        if (isPublic) {
            datamanagementPlan.setDatasets(datamanagementPlan.getDatasets().stream().filter(dataset -> dataset.getStatus() == Dataset.Status.FINALISED.getValue()).collect(Collectors.toList()));
        }

        return datamanagementPlan;
    }

    public List<Tuple<String, String>> getDynamicFields(String id, DynamicGrantConfiguration dynamicGrantConfiguration, DynamicFieldsCriteria criteria) throws IllegalAccessException, InstantiationException {
        List<Tuple<String, String>> result = new LinkedList<>();
        RestTemplate restTemplate = new RestTemplate();
        HttpHeaders headers = new HttpHeaders();
        headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
        headers.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<String> entity = new HttpEntity<>("parameters", headers);

        Property property = dynamicGrantConfiguration.getConfiguration().getConfigurationProperties().stream()
                                                     .filter(item -> item.getId().equals(id)).findFirst().orElse(null);
        StringBuilder stringBuilder = new StringBuilder();
        if (criteria.getLike() != null) stringBuilder.append("?search=" + criteria.getLike());
        if (property.getDependencies() != null && !property.getDependencies().isEmpty() && criteria.getDynamicFields() != null && !criteria.getDynamicFields().isEmpty()) {
            property.getDependencies().stream().forEach(item -> {
                DynamicFieldsCriteria.DynamicFieldDependencyCriteria dependencyCriteria = criteria.getDynamicFields().stream().filter(dfield -> dfield.getProperty().equals(item.getId()))
                                                                                                  .findFirst().orElse(null);
                if (dependencyCriteria != null) {
                    if (criteria.getLike() != null || property.getDependencies().indexOf(item) > 0)
                        stringBuilder.append("&");
                    stringBuilder.append(item.getQueryProperty() + "=" + dependencyCriteria.getValue());
                }
            });

            ResponseEntity<ArrayList> response = restTemplate.exchange(property.getSourceUrl() + stringBuilder.toString(), HttpMethod.GET, entity, ArrayList.class);
            response.getBody().forEach(item -> {
                Tuple<String, String> tuple = new Tuple<>();
                tuple.setId((String) (((Map<String, Object>) item).get(property.getExternalFieldId())));
                tuple.setLabel((String) (((Map<String, Object>) item).get(property.getExternalFieldLabel())));
                result.add(tuple);
            });

        } else {
            ResponseEntity<ArrayList> response = restTemplate.exchange(property.getSourceUrl() + stringBuilder.toString(), HttpMethod.GET, entity, ArrayList.class);
            response.getBody().forEach(item -> {
                Tuple<String, String> tuple = new Tuple<>();
                tuple.setId((String) (((Map<String, Object>) item).get(property.getExternalFieldId())));
                tuple.setLabel((String) (((Map<String, Object>) item).get(property.getExternalFieldLabel())));
                result.add(tuple);
            });
        }
        return result;
    }

    public DataTableData<DatasetProfileListingModel> getDatasetProfilesUsedByDMP(DatasetProfileTableRequestItem datasetProfileTableRequestItem, Principal principal) {
        datasetProfileTableRequestItem.getCriteria().setFilter(DatasetProfileCriteria.DatasetProfileFilter.DMPs.getValue());
        datasetProfileTableRequestItem.getCriteria().setUserId(principal.getId());

        QueryableList<DatasetProfile> items = apiContext.getOperationsContext().getDatabaseRepository().getDatasetProfileDao().getWithCriteria(datasetProfileTableRequestItem.getCriteria());
        List<DatasetProfileListingModel> listingModels = items.select(item -> new DatasetProfileListingModel().fromDataModel(item));

        DataTableData<DatasetProfileListingModel> data = new DataTableData<>();
        data.setData(listingModels);
        data.setTotalCount((long) listingModels.size());

        return data;
    }

    public List<VersionListingModel> getAllVersions(String groupId, Principal principal, Boolean isPublic) {
        UUID principalId = principal != null ? principal.getId() : null;
        List<VersionListingModel> versions = new ArrayList<>();
        QueryableList<DMP> items = null;
        QueryableList<DMP> authItems = null;
        List<Integer> roles = new LinkedList<>();
        DataManagementPlanCriteria criteria = new DataManagementPlanCriteria();
        criteria.setGroupIds(Collections.singletonList(UUID.fromString(groupId)));
        criteria.setAllVersions(true);
        criteria.setIsPublic(isPublic);
        criteria.setOnlyPublic(isPublic);
        items = apiContext.getOperationsContext().getDatabaseRepository().getDmpDao().getWithCriteria(criteria);
        if (!isPublic) {
            authItems = apiContext.getOperationsContext().getDatabaseRepository().getDmpDao().getAuthenticated(items, principalId, roles);
        } else {
            authItems = items;
        }
        CompletableFuture<List<VersionListingModel>> versionFuture = authItems.withHint(HintedModelFactory.getHint(VersionListingModel.class))
                 .orderBy(((builder, root) -> builder.desc(root.get("version"))))
                 .selectAsync(item -> new VersionListingModel().fromDataModel(item))
                 .whenComplete(((versionListingModels, throwable) -> versions.addAll(versionListingModels)));

        CompletableFuture.allOf(versionFuture).join();

        return versions;
    }

    /*
    * Data Management
    * */

    public DMP createOrUpdate(DataManagementPlanEditorModel dataManagementPlan, Principal principal) throws Exception {
        boolean setNotification = false;
        if (dataManagementPlan.getId() != null) {
            DMP dmp1 = apiContext.getOperationsContext().getDatabaseRepository().getDmpDao().find(dataManagementPlan.getId());
            
            Instant dbTime = Instant.ofEpochMilli(dmp1.getModified().getTime()).truncatedTo(ChronoUnit.SECONDS);
            Instant modelTime = Instant.ofEpochMilli(dataManagementPlan.getModified().getTime()).truncatedTo(ChronoUnit.SECONDS);

            if (!isUserOwnerOfDmp(dmp1, principal)) {
                throw new Exception("User not being the creator is not authorized to edit this DMP.");
            }
            if (dbTime.toEpochMilli() != modelTime.toEpochMilli()) {
                throw new Exception("Another user have already edit that DMP.");
            }
            List<Dataset> datasetList = dmp1.getDataset().stream().filter(dataset -> dataset.getStatus() != 99).collect(Collectors.toList());
            for (Dataset dataset : datasetList) {
                if (dataManagementPlan.getProfiles().stream().filter(associatedProfile -> dataset.getProfile().getId().equals(associatedProfile.getId())).findAny().orElse(null) == null)
                    throw new Exception("Dataset Template for Dataset Description is missing from the DMP.");
            }
            if (dataManagementPlan.getStatus() == (int) DMP.DMPStatus.FINALISED.getValue() && dmp1.getStatus().equals(DMP.DMPStatus.FINALISED.getValue()))
                throw new Exception("DMP is finalized, therefore cannot be edited.");

            setNotification = true;
        } else {
            metricsManager.increaseValue(MetricNames.DMP, 1, MetricNames.DRAFT);
        }

        DMP newDmp = dataManagementPlan.toDataModel();
        if (newDmp.getStatus() == (int) DMP.DMPStatus.FINALISED.getValue()) {
            checkDmpValidationRules(newDmp);
        }
        UserInfo user = apiContext.getOperationsContext().getDatabaseRepository().getUserInfoDao().find(principal.getId());

        createOrganisationsIfTheyDontExist(newDmp, apiContext.getOperationsContext().getDatabaseRepository().getOrganisationDao());
        createResearchersIfTheyDontExist(newDmp, apiContext.getOperationsContext().getDatabaseRepository().getResearcherDao(), user);
        createFunderIfItDoesntExist(newDmp, apiContext.getOperationsContext().getDatabaseRepository().getFunderDao());
        createGrantIfItDoesntExist(newDmp, apiContext.getOperationsContext().getDatabaseRepository().getGrantDao());
        if (newDmp.getProject().getLabel() == null || newDmp.getProject().getLabel().trim().isEmpty()) {
            newDmp.setProject(newDmp.getProject().projectFromGrant(newDmp.getGrant()));
        }
        createProjectIfItDoesntExist(newDmp, apiContext.getOperationsContext().getDatabaseRepository().getProjectDao());

        DMP dmp;
        if (dataManagementPlan.getId() != null) {
            dmp = apiContext.getOperationsContext().getDatabaseRepository().getDmpDao().find(dataManagementPlan.getId());
        } else dmp = new DMP();

        newDmp.setCreated(dmp.getCreated() == null ? new Date() : dmp.getCreated());
        if (newDmp.getUsers()!= null && newDmp.getUsers().stream().filter(userInfo -> userInfo.getUser().getId() == principal.getId())
                                              .collect(Collectors.toList()).size() == 0) {
            List<UserDMP> userDMPList = new ArrayList<>(newDmp.getUsers());
            for (UserInfoListingModel userInfoListingModel : dataManagementPlan.getUsers()) {
                for (UserDMP userDMP : userDMPList) {
                    if (!(userDMP.getUser().getId().equals(userInfoListingModel.getId()))) {
                        apiContext.getOperationsContext().getDatabaseRepository().getUserDmpDao().delete(userDMP);
                    }
                }
            }
        }

        if (newDmp.getGrant().getType().equals(Grant.GrantType.INTERNAL.getValue())) {
            checkIfUserCanEditGrant(newDmp, user);
        }
        assignGrandUserIfInternal(newDmp, user);
        assignFunderUserIfInternal(newDmp, user);
        assignProjectUserIfInternal(newDmp, user);

        apiContext.getOperationsContext().getDatabaseRepository().getGrantDao().createOrUpdate(newDmp.getGrant());
        newDmp = apiContext.getOperationsContext().getDatabaseRepository().getDmpDao().createOrUpdate(newDmp);

        if (dataManagementPlan.getUsers() != null && !dataManagementPlan.getUsers().isEmpty()) {
            clearUsers(newDmp);
            for (UserInfoListingModel userListing : dataManagementPlan.getUsers()) {
                UserInfo tempUser = apiContext.getOperationsContext().getDatabaseRepository().getUserInfoDao().find(userListing.getId());
                assignUser(dmp, tempUser, UserDMP.UserDMPRoles.fromInteger(userListing.getRole()));
            }
        }

        // Dataset manipulation for when the DMP is set to be finalized.
        if (dataManagementPlan.getStatus() == DMP.DMPStatus.FINALISED.getValue()) {
            if (dataManagementPlan.getDatasetsToBeFinalized() != null && !dataManagementPlan.getDatasetsToBeFinalized().isEmpty()) {
                apiContext.getOperationsContext().getDatabaseRepository().getDatasetDao()
                          .asQueryable().where((builder, root) -> root.get("id").in(dataManagementPlan.getDatasetsToBeFinalized()))
                          .update(root -> root.<Integer>get("status"), Dataset.Status.FINALISED.getValue());
                apiContext.getOperationsContext().getDatabaseRepository().getDatasetDao()
                          .asQueryable().where((builder, root) -> root.get("id").in(dataManagementPlan.getDatasetsToBeFinalized()))
                          .update(root -> root.<Date>get("finalizedat"), new Date());

                List<UUID> datasetsToBeCanceled = new LinkedList<>();
                for (DatasetWizardModel dataset : dataManagementPlan.getDatasets()) {
                    if (!(dataset.getStatus() == (int) Dataset.Status.FINALISED.getValue()) && !dataManagementPlan.getDatasetsToBeFinalized().contains(dataset.getId())) {
                        datasetsToBeCanceled.add(UUID.fromString(dataset.getId().toString()));
                    }
                }
                if (!datasetsToBeCanceled.isEmpty())
                    apiContext.getOperationsContext().getDatabaseRepository().getDatasetDao()
                              .asQueryable().where((builder, root) -> root.get("id").in(datasetsToBeCanceled))
                              .update(root -> root.<Integer>get("status"), Dataset.Status.CANCELED.getValue());
            } else {
                List<UUID> datasetsToBeCanceled = new LinkedList<>();
                for (DatasetWizardModel dataset : dataManagementPlan.getDatasets()) {
                    if (!(dataset.getStatus() == (int) Dataset.Status.FINALISED.getValue())) {
                        datasetsToBeCanceled.add(dataset.getId());
                    }
                }
                if (!datasetsToBeCanceled.isEmpty())
                    apiContext.getOperationsContext().getDatabaseRepository().getDatasetDao()
                              .asQueryable().where((builder, root) -> root.get("id").in(datasetsToBeCanceled))
                              .update(root -> root.<Integer>get("status"), Dataset.Status.CANCELED.getValue());
            }
        }

        if (dataManagementPlan.getAssociatedUsers().size() == 0)
            assignUser(newDmp, user);

        UUID dmpId = newDmp.getId();
        newDmp.setUsers(new HashSet<>(apiContext.getOperationsContext().getDatabaseRepository().getUserDmpDao().asQueryable().where((builder, root) -> builder.equal(root.get("dmp").get("id"), dmpId)).toList()));

        this.updateIndex(newDmp);

        if (setNotification) {
            if (newDmp.getStatus() != DMP.DMPStatus.FINALISED.getValue()) {
                this.sendNotification(newDmp, user, NotificationType.DMP_MODIFIED);
            } else {
                this.sendNotification(newDmp, user, NotificationType.DMP_MODIFIED_FINALISED);
            }
        }

        return newDmp;
    }

    public DMP createOrUpdateWithDatasets(DataManagementPlanEditorModel dataManagementPlan, Principal principal) throws Exception {
        if (dataManagementPlan.getId() != null) {
            DMP dmp1 = apiContext.getOperationsContext().getDatabaseRepository().getDmpDao().find(dataManagementPlan.getId());
            
            Instant dbTime = Instant.ofEpochMilli(dmp1.getModified().getTime()).truncatedTo(ChronoUnit.SECONDS);
            Instant modelTime = Instant.ofEpochMilli(dataManagementPlan.getModified().getTime()).truncatedTo(ChronoUnit.SECONDS);

            if (!isUserOwnerOfDmp(dmp1, principal)) {
                throw new Exception("User not being the creator is not authorized to edit this DMP.");
            }
            if (dbTime.toEpochMilli() != modelTime.toEpochMilli()) {
                throw new Exception("Another user have already edit that DMP.");
            }
            for (DatasetWizardModel dataset : dataManagementPlan.getDatasets()) {
                if (dataManagementPlan.getProfiles().stream().filter(associatedProfile -> dataset.getProfile().getId().equals(associatedProfile.getId())).findAny().orElse(null) == null)
                    throw new Exception("Dataset Template for Dataset Description is missing from the DMP.");
            }
            if (dataManagementPlan.getStatus() == (int) DMP.DMPStatus.FINALISED.getValue() && dmp1.getStatus().equals(DMP.DMPStatus.FINALISED.getValue()))
                throw new Exception("DMP is finalized, therefore cannot be edited.");
        } else {
            metricsManager.increaseValue(MetricNames.DMP, 1, MetricNames.DRAFT);
        }
        List<Dataset> datasets = new ArrayList<>();
        DMP tempDMP = dataManagementPlan.toDataModel();
        if (tempDMP.getStatus() == (int) DMP.DMPStatus.FINALISED.getValue()) {
            checkDmpValidationRules(tempDMP);
        }
        UserInfo user = apiContext.getOperationsContext().getDatabaseRepository().getUserInfoDao().find(principal.getId());
        createOrganisationsIfTheyDontExist(tempDMP, apiContext.getOperationsContext().getDatabaseRepository().getOrganisationDao());
        createResearchersIfTheyDontExist(tempDMP, apiContext.getOperationsContext().getDatabaseRepository().getResearcherDao(), user);
        createFunderIfItDoesntExist(tempDMP, apiContext.getOperationsContext().getDatabaseRepository().getFunderDao());
        createGrantIfItDoesntExist(tempDMP, apiContext.getOperationsContext().getDatabaseRepository().getGrantDao());

        DMP result = createOrUpdate(dataManagementPlan, principal);

        for (DatasetWizardModel datasetWizardModel: dataManagementPlan.getDatasets()) {
            datasetWizardModel.setDmp(new DataManagementPlan().fromDataModel(result));
            Dataset dataset = datasetManager.createOrUpdate(datasetWizardModel, principal);
            datasets.add(dataset);
        }

        UUID dmpId = result.getId();
        result.setUsers(new HashSet<>(apiContext.getOperationsContext().getDatabaseRepository().getUserDmpDao().asQueryable().where((builder, root) -> builder.equal(root.get("dmp").get("id"), dmpId)).toList()));

        this.updateIndex(result);

        return result;
    }

    public UUID newVersion(UUID uuid, DataManagementPlanNewVersionModel dataManagementPlan, Principal principal) throws Exception {
        DMP oldDmp = databaseRepository.getDmpDao().find(uuid);
        if (!isUserOwnerOfDmp(oldDmp, principal)) {
            throw new Exception("User not being the creator is not authorized to perform this action.");
        }
        DataManagementPlanCriteria criteria = new DataManagementPlanCriteria();
        LinkedList<UUID> list = new LinkedList<>();
        list.push(oldDmp.getGroupId());
        criteria.setGroupIds(list);
        criteria.setAllVersions(false);
        QueryableList<DMP> dataManagementPlanQueryableList = databaseRepository.getDmpDao().getWithCriteria(criteria);
        List<DMP> latestVersionDMP = dataManagementPlanQueryableList.toList();

        if (latestVersionDMP.get(0).getVersion().equals(oldDmp.getVersion())) {
            DMP newDmp = dataManagementPlan.toDataModel();
            UserInfo user = apiContext.getOperationsContext().getBuilderFactory().getBuilder(UserInfoBuilder.class).id(principal.getId()).build();
            createOrganisationsIfTheyDontExist(newDmp, databaseRepository.getOrganisationDao());
            createResearchersIfTheyDontExist(newDmp, databaseRepository.getResearcherDao(), user);

            createFunderIfItDoesntExist(newDmp, databaseRepository.getFunderDao());
            createGrantIfItDoesntExist(newDmp, databaseRepository.getGrantDao());
            if (newDmp.getProject().getLabel() == null || newDmp.getProject().getLabel().trim().isEmpty()) {
                newDmp.setProject(newDmp.getProject().projectFromGrant(newDmp.getGrant()));
            }
            createProjectIfItDoesntExist(newDmp, databaseRepository.getProjectDao());

            newDmp.setGroupId(oldDmp.getGroupId());
            newDmp.setVersion(oldDmp.getVersion() + 1);
            newDmp.setId(null);

            checkIfUserCanEditGrant(newDmp, user);
            assignGrandUserIfInternal(newDmp, user);
            assignFunderUserIfInternal(newDmp, user);
            assignProjectUserIfInternal(newDmp, user);
            if (newDmp.getGrant().getStartdate() == null) {
                newDmp.getGrant().setStartdate(new Date());
            }
            if (newDmp.getGrant().getEnddate() == null) {
                newDmp.getGrant().setEnddate(Date.from(Instant.now().plus(365, ChronoUnit.DAYS)));
            }
            databaseRepository.getGrantDao().createOrUpdate(newDmp.getGrant());
            DMP tempDmp = databaseRepository.getDmpDao().createOrUpdate(newDmp);
            newDmp.setId(tempDmp.getId());

            // Assign creator.
            //assignUser(newDmp, user);

            copyDatasets(newDmp, databaseRepository.getDatasetDao());

           databaseRepository
                    .getUserDmpDao().asQueryable().where((builder, root) -> builder.equal(root.get("dmp").get("id"), oldDmp.getId()))
                    .toList().stream().forEach(userDMP -> {
                        UserDMP temp = new UserDMP();
                        temp.setUser(userDMP.getUser());
                        temp.setRole(userDMP.getRole());
                        temp.setDmp(newDmp);
                        apiContext.getOperationsContext().getDatabaseRepository().getUserDmpDao().createOrUpdate(temp);
                    });

            newDmp.setUsers(new HashSet<>(databaseRepository.getUserDmpDao().asQueryable().where((builder, root) -> builder.equal(root.get("dmp").get("id"), newDmp.getId())).toList()));

            DatasetCriteria criteria1 = new DatasetCriteria();
            criteria1.setDmpIds(Collections.singletonList(newDmp.getId()));
            newDmp.setDataset(new HashSet<>(databaseRepository.getDatasetDao().getWithCriteria(criteria1).toList()));

            this.updateGroupIndex(newDmp.getGroupId());
            return newDmp.getId();
        } else {
            throw new DMPNewVersionException("Version to update not the latest.");
        }
    }

    public UUID clone(DataManagementPlanNewVersionModel dataManagementPlan, Principal principal) throws Exception {
        DMP newDmp = dataManagementPlan.toDataModel();

        UserInfo user = apiContext.getOperationsContext().getBuilderFactory().getBuilder(UserInfoBuilder.class).id(principal.getId()).build();
        createOrganisationsIfTheyDontExist(newDmp, databaseRepository.getOrganisationDao());
        createResearchersIfTheyDontExist(newDmp, databaseRepository.getResearcherDao(), user);

        createFunderIfItDoesntExist(newDmp, databaseRepository.getFunderDao());
        createGrantIfItDoesntExist(newDmp, databaseRepository.getGrantDao());
        if (newDmp.getProject().getLabel() == null || newDmp.getProject().getLabel().trim().isEmpty()) {
            newDmp.setProject(newDmp.getProject().projectFromGrant(newDmp.getGrant()));
        }
        createProjectIfItDoesntExist(newDmp, databaseRepository.getProjectDao());

        newDmp.setGroupId(UUID.randomUUID());
        newDmp.setVersion(0);
        newDmp.setId(null);

        checkIfUserCanEditGrant(newDmp, user);
        assignGrandUserIfInternal(newDmp, user);
        assignFunderUserIfInternal(newDmp, user);
        assignProjectUserIfInternal(newDmp, user);
        databaseRepository.getGrantDao().createOrUpdate(newDmp.getGrant());
        DMP tempDmp = databaseRepository.getDmpDao().createOrUpdate(newDmp);
        newDmp.setId(tempDmp.getId());

        assignUser(newDmp, user);
        copyDatasets(newDmp, databaseRepository.getDatasetDao());

        newDmp.setUsers(new HashSet<>(databaseRepository.getUserDmpDao().asQueryable().where((builder, root) -> builder.equal(root.get("dmp").get("id"), newDmp.getId())).toList()));

        DatasetCriteria criteria1 = new DatasetCriteria();
        criteria1.setDmpIds(Collections.singletonList(newDmp.getId()));
        newDmp.setDataset(new HashSet<>(databaseRepository.getDatasetDao().getWithCriteria(criteria1).toList()));
        UUID dmpId = newDmp.getId();
        newDmp.setUsers(new HashSet<>(apiContext.getOperationsContext().getDatabaseRepository().getUserDmpDao().asQueryable().where((builder, root) -> builder.equal(root.get("dmp").get("id"), dmpId)).toList()));

        this.updateIndex(newDmp);
        metricsManager.increaseValue(MetricNames.DMP, 1, MetricNames.DRAFT);

        return newDmp.getId();
    }

    public void delete(UUID uuid) throws DMPWithDatasetsDeleteException, IOException {
        DatasetCriteria criteria = new DatasetCriteria();
        List<UUID> dmpIds = Collections.singletonList(uuid);
        criteria.setDmpIds(dmpIds);
        if (apiContext.getOperationsContext().getDatabaseRepository().getDatasetDao().getWithCriteria(criteria).toList().size() > 0)
            throw new DMPWithDatasetsDeleteException("You cannot Remove Datamanagement Plan with Datasets");
        DMP oldDmp = apiContext.getOperationsContext().getDatabaseRepository().getDmpDao().find(uuid);
        switch (oldDmp.getStatus()) {
            case 0:
                metricsManager.decreaseValue(MetricNames.DMP, 1, MetricNames.DRAFT);
                break;
            case 1:
                if (oldDmp.getDoi() != null) {
                    metricsManager.decreaseValue(MetricNames.DMP, 1, MetricNames.DOIED);
                }
                if (oldDmp.isPublic()) {
                    metricsManager.decreaseValue(MetricNames.DMP, 1, MetricNames.PUBLISHED);
                }
                metricsManager.decreaseValue(MetricNames.DMP, 1, MetricNames.FINALIZED);
                break;
        }
        oldDmp.setStatus(DMP.DMPStatus.DELETED.getValue());
        apiContext.getOperationsContext().getDatabaseRepository().getDmpDao().createOrUpdate(oldDmp);
        UUID dmpId = oldDmp.getId();
        oldDmp.setUsers(new HashSet<>(apiContext.getOperationsContext().getDatabaseRepository().getUserDmpDao().asQueryable().where((builder, root) -> builder.equal(root.get("dmp").get("id"), dmpId)).toList()));
        this.updateIndex(oldDmp);
        DataManagementPlanCriteria criteria1 = new DataManagementPlanCriteria();
        criteria1.setAllVersions(true);
        criteria1.setGroupIds(Collections.singletonList(oldDmp.getGroupId()));
        apiContext.getOperationsContext().getDatabaseRepository().getDmpDao().getWithCriteria(criteria1).toList().forEach(dmp -> {
            try {
                UUID tdmpId = dmp.getId();
                dmp.setUsers(new HashSet<>(apiContext.getOperationsContext().getDatabaseRepository().getUserDmpDao().asQueryable().where((builder, root) -> builder.equal(root.get("dmp").get("id"), tdmpId)).toList()));
                this.updateIndex(dmp);
            } catch (IOException e) {
                logger.error(e.getMessage(), e);
            }
            for (Dataset dataset: dmp.getDataset()) {
                try {
                    List<Tag> tags = new ArrayList<>();
                    eu.eudat.elastic.entities.Dataset elastic = apiContext.getOperationsContext().getElasticRepository().getDatasetRepository().findDocument(dataset.getId().toString());
                    if (elastic != null) {
                        tags = elastic.getTags();
                    }
                    dataset.setDmp(dmp);
                    this.datasetManager.updateTags(dataset, tags);
                } catch (Exception e) {
                    logger.error(e.getMessage(), e);
                }
            }
        });
    }

    private void createResearchersIfTheyDontExist(DMP newDmp, ResearcherDao researcherRepository, UserInfo user) {
        if (newDmp.getResearchers() != null && !newDmp.getResearchers().isEmpty()) {
            for (eu.eudat.data.entities.Researcher researcher : newDmp.getResearchers()) {
                ResearcherCriteria criteria = new ResearcherCriteria();
                criteria.setReference(researcher.getReference());
                List<eu.eudat.data.entities.Researcher> entries = researcherRepository.getWithCriteria(criteria).toList();
                if (entries != null && !entries.isEmpty()) researcher.setId(entries.get(0).getId());
                else {
                    researcher.setCreationUser(user);
                    researcherRepository.createOrUpdate(researcher);
                    metricsManager.increaseValue(MetricNames.RESEARCHER, 1, null);
                }
            }
        }
    }

    private void createOrganisationsIfTheyDontExist(DMP newDmp, OrganisationDao organisationRepository) {
        if (newDmp.getOrganisations() != null && !newDmp.getOrganisations().isEmpty()) {
            for (eu.eudat.data.entities.Organisation organisation : newDmp.getOrganisations()) {
                boolean createNew = false;
                if (organisation.getReference() != null) {
                    OrganisationCriteria criteria = new OrganisationCriteria();
                    criteria.setLike(organisation.getReference());
                    List<eu.eudat.data.entities.Organisation> entries = organisationRepository.getWithCriteria(criteria).toList();
                    if (entries != null && !entries.isEmpty()) organisation.setId(entries.get(0).getId());
                    else createNew = true;
                } else {
                    createNew = true;
                }

                if (createNew) {
                    if (organisation.getReference() == null) {
                        organisation.setReference("Internal:" + UUID.randomUUID().toString());
                    }
                    organisationRepository.createOrUpdate(organisation);
                }
            }
        }
    }

    private void createGrantIfItDoesntExist(DMP newDmp, GrantDao grantDao) {
        if (newDmp.getGrant() != null) {
            Grant grant = newDmp.getGrant();
            GrantCriteria criteria = new GrantCriteria();
            if (grant.getReference() != null) {
                criteria.setReference(grant.getReference());
                eu.eudat.data.entities.Grant grantEntity = grantDao.getWithCriteria(criteria).toList().stream().max(Comparator.comparing(grant1 -> grant1.getModified().getTime())).orElse(null);
                if (grantEntity != null) grant.setId(grantEntity.getId());
                else {
                    grant.setType(Grant.GrantType.EXTERNAL.getValue());
                    grant.setCreationUser(null);
                    if (grant.getStartdate() == null) {
                        grant.setStartdate(new Date());
                    }
                    if (grant.getEnddate() == null) {
                        grant.setEnddate(Date.from(Instant.now().plus(365, ChronoUnit.DAYS)));
                    }
                    grantDao.createOrUpdate(grant);
                }
            }
            else {
                grant.setType(Grant.GrantType.EXTERNAL.getValue());
                grant.setCreationUser(null);
                grantDao.createOrUpdate(grant);
            }
        }
    }

    private void createFunderIfItDoesntExist(DMP newDmp, FunderDao funderDao) {
        if (newDmp.getGrant().getFunder() != null) {
            Funder funder = newDmp.getGrant().getFunder();
            FunderCriteria criteria = new FunderCriteria();
            if (funder.getReference() != null) {
                criteria.setReference(funder.getReference());
                eu.eudat.data.entities.Funder funderEntity = funderDao.getWithCritetia(criteria).toList().stream().max(Comparator.comparing(funder1 -> funder1.getModified().getTime())).orElse(null);
                if (funderEntity != null) funder.setId(funderEntity.getId());
                else {
                    funderDao.createOrUpdate(funder);
                }
            }
            else {
                funderDao.createOrUpdate(funder);
            }
        }
    }

    private void createProjectIfItDoesntExist(DMP newDmp, ProjectDao projectDao) {
        if (newDmp.getProject() != null) {
            Project project = newDmp.getProject();
            ProjectCriteria criteria = new ProjectCriteria();
            if (project.getReference() != null) {
                criteria.setReference(project.getReference());
                eu.eudat.data.entities.Project projectEntity = projectDao.getWithCritetia(criteria).toList().stream().max(Comparator.comparing(project1 -> project1.getModified().getTime())).orElse(null);
                if (projectEntity != null) project.setId(projectEntity.getId());
                else {
                    createExternalProject(project, projectDao);
                }
            }
            else {
                createExternalProject(project, projectDao);
            }
        }
    }

    private void createExternalProject(Project project, ProjectDao projectDao) {
        if (project.getStartdate() == null) project.setStartdate(new Date());
        if (project.getEnddate() == null) project.setEnddate(new Date());
        project.setType(Project.ProjectType.EXTERNAL.getValue());
        if (project.getId() == null) project.setId(UUID.randomUUID());
        projectDao.createOrUpdate(project);
        metricsManager.increaseValue(MetricNames.PROJECT, 1, null);
    }

    private void copyDatasets(DMP newDmp, DatasetDao datasetDao) {
        List<CompletableFuture<Dataset>> futures = new LinkedList<>();
        for (Dataset dataset : newDmp.getDataset()) {
            Dataset tempDataset = datasetDao.find(dataset.getId());
            try {
                List<Tag> tags = new ArrayList<>();
                eu.eudat.elastic.entities.Dataset elastic = apiContext.getOperationsContext().getElasticRepository().getDatasetRepository().findDocument(dataset.getId().toString());
                if (elastic != null) {
                    tags = elastic.getTags();
                }
                UUID dmpId = tempDataset.getDmp().getId();
                tempDataset.getDmp().setUsers(new HashSet<>(apiContext.getOperationsContext().getDatabaseRepository().getUserDmpDao().asQueryable().where((builder, root) -> builder.equal(root.get("dmp").get("id"), dmpId)).toList()));
                this.datasetManager.updateTags(tempDataset, tags);
            } catch (Exception e) {
                logger.error(e.getMessage(), e);
            }
            datasetDao.asQueryable().withHint(HintedModelFactory.getHint(DatasetListingModel.class)).where((builder, root) -> builder.equal(root.get("id"), dataset.getId())).getSingleAsync()
                      .thenApplyAsync(entityDataset -> {
                          Dataset newDataset = new Dataset();
                          newDataset.update(entityDataset);
                          newDataset.setDmp(newDmp);
                          newDataset.setStatus(Dataset.Status.SAVED.getValue());
                          if (newDataset.getDatasetDataRepositories() != null) {
                              newDataset.setDatasetDataRepositories(newDataset.getDatasetDataRepositories().stream().map(item -> {
                                  DataRepository dataRepository = new DataRepository();
                                  dataRepository.setId(item.getDataRepository().getId());

                                  DatasetDataRepository datasetDataRepository = new DatasetDataRepository();
                                  datasetDataRepository.setDataRepository(dataRepository);
                                  datasetDataRepository.setDataset(newDataset);
                                  datasetDataRepository.setData(item.getData());
                                  return datasetDataRepository;
                              }).collect(Collectors.toSet()));
                          }

                          if (newDataset.getDatasetExternalDatasets() != null) {
                              newDataset.setDatasetExternalDatasets(newDataset.getDatasetExternalDatasets().stream().map(item -> {
                                  ExternalDataset externalDataset = new ExternalDataset();
                                  externalDataset.setId(item.getExternalDataset().getId());
                                  DatasetExternalDataset datasetExternalDataset = new DatasetExternalDataset();
                                  datasetExternalDataset.setExternalDataset(externalDataset);
                                  datasetExternalDataset.setDataset(newDataset);
                                  datasetExternalDataset.setData(item.getData());
                                  return datasetExternalDataset;
                              }).collect(Collectors.toSet()));
                          }

                          if (newDataset.getRegistries() != null) {
                              newDataset.setRegistries(newDataset.getRegistries().stream().map(item -> {
                                  Registry registry = new Registry();
                                  registry.setId(item.getId());
                                  return registry;
                              }).collect(Collectors.toSet()));
                          }

                          if (newDataset.getServices() != null) {
                              newDataset.setServices(newDataset.getServices().stream().map(item -> {
                                  Service service = new Service();
                                  service.setId(item.getId());
                                  DatasetService datasetService = new DatasetService();
                                  datasetService.setService(service);
                                  datasetService.setDataset(newDataset);
                                  datasetService.setData(item.getData());
                                  return datasetService;
                              }).collect(Collectors.toSet()));
                          }
                          newDataset.setCreated(new Date());
                          return newDataset;
                      }).thenApplyAsync(item -> {
                futures.add(datasetDao.createOrUpdateAsync(item).whenComplete(((dataset1, throwable) -> {
                    metricsManager.increaseValue(MetricNames.DATASET, 1, MetricNames.DRAFT);
                    eu.eudat.elastic.entities.Dataset datasetElastic = new eu.eudat.elastic.entities.Dataset();
                    datasetElastic.setId(dataset1.getId().toString());
                    datasetElastic.setLabel(dataset1.getLabel());
                    datasetElastic.setDescription(dataset1.getDescription());
                    datasetElastic.setTemplate(dataset1.getProfile().getId());
                    datasetElastic.setStatus(dataset1.getStatus());
                    datasetElastic.setDmp(dataset1.getDmp().getId());
                    datasetElastic.setGroup(dataset1.getDmp().getGroupId());
                    datasetElastic.setGrant(dataset1.getDmp().getGrant().getId());
                    if (dataset1.getDmp().getUsers() != null) {
                        datasetElastic.setCollaborators(dataset1.getDmp().getUsers().stream().map(user -> {
                            Collaborator collaborator = new Collaborator();
                            collaborator.setId(user.getId().toString());
                            collaborator.setRole(user.getRole());
                            //  collaborator.setName(user.getUser().getName());
                            return collaborator;
                        }).collect(Collectors.toList()));
                    }
                    datasetElastic.setLastVersion(true);
                    datasetElastic.setLastPublicVersion(false);
                    if (dataset1.getDmp().getOrganisations() != null) {
                        datasetElastic.setOrganizations(dataset1.getDmp().getOrganisations().stream().map(org -> {
                            Organization organization = new Organization();
                            organization.setId(org.getId().toString());
                            organization.setName(org.getLabel());
                            return organization;
                        }).collect(Collectors.toList()));
                    }
                    datasetElastic.setPublic(dataset1.getDmp().isPublic());
                    datasetElastic.setGrantStatus(dataset1.getDmp().getGrant().getStatus());

                    try {
                        eu.eudat.elastic.entities.Dataset oldDatasetElastic = apiContext.getOperationsContext().getElasticRepository().getDatasetRepository().findDocument(dataset.getId().toString());
                        if (oldDatasetElastic != null) {
                            datasetElastic.setTags(oldDatasetElastic.getTags());
                        }
                        datasetElastic.setFormData(this.datasetManager.getWordDocumentText(dataset1));
                        apiContext.getOperationsContext().getElasticRepository().getDatasetRepository().createOrUpdate(datasetElastic);
                    } catch (Exception e) {
                        logger.error(e.getMessage(), e);
                    }
                })));
                return futures;
            }).join();

        }
    }

    public void makePublic(UUID id, Principal principal) throws Exception {
        DMP dmp = this.apiContext.getOperationsContext().getDatabaseRepository().getDmpDao().find(id);
        // Check if dmp is finalized and if user is owner.
        if (!isUserOwnerOfDmp(dmp, principal))
            throw new Exception("User does not have the privilege to do this action.");
        if (!dmp.getStatus().equals(DMP.DMPStatus.FINALISED.getValue()))
            throw new Exception("DMP is not finalized");
        dmp.setPublic(true);
        apiContext.getOperationsContext().getDatabaseRepository().getDmpDao().createOrUpdate(dmp);
        UUID dmpId = dmp.getId();
        dmp.setUsers(new HashSet<>(apiContext.getOperationsContext().getDatabaseRepository().getUserDmpDao().asQueryable().where((builder, root) -> builder.equal(root.get("dmp").get("id"), dmpId)).toList()));
        this.updateIndex(dmp);
        metricsManager.increaseValue(MetricNames.DMP, 1, MetricNames.PUBLISHED);
        DataManagementPlanCriteria criteria = new DataManagementPlanCriteria();
        criteria.setGroupIds(Collections.singletonList(dmp.getGroupId()));
        criteria.setAllVersions(true);
        apiContext.getOperationsContext().getDatabaseRepository().getDmpDao().getWithCriteria(criteria).toList().stream().forEach(dmp1 -> {
            dmp1.getDataset().forEach(dataset -> {
                try {
                    List<Tag> tags = new ArrayList<>();
                    eu.eudat.elastic.entities.Dataset elastic = apiContext.getOperationsContext().getElasticRepository().getDatasetRepository().findDocument(dataset.getId().toString());
                    if (elastic != null) {
                        tags = elastic.getTags();
                    }
                    UUID tmdmpId = dataset.getDmp().getId();
                    dataset.getDmp().setUsers(new HashSet<>(apiContext.getOperationsContext().getDatabaseRepository().getUserDmpDao().asQueryable().where((builder, root) -> builder.equal(root.get("dmp").get("id"), tmdmpId)).toList()));
                    this.datasetManager.updateTags(dataset, tags);
                    metricsManager.increaseValue(MetricNames.DATASET, 1, MetricNames.PUBLISHED);
                } catch (Exception e) {
                    logger.error(e.getMessage(), e);
                }
            });
        });
        UserInfo user = apiContext.getOperationsContext().getDatabaseRepository().getUserInfoDao().find(principal.getId());
        sendNotification(dmp, user, NotificationType.DMP_PUBLISH);
//        if (dmp.getDois() != null && !dmp.getDois().isEmpty()) {
//            this.createZenodoDoi(dmp.getId(), principal, true);
//        }
    }

    @Transactional
    public void makeFinalize(UUID id, Principal principal, DatasetsToBeFinalized datasetsToBeFinalized) throws Exception {
        DMP dmp = this.apiContext.getOperationsContext().getDatabaseRepository().getDmpDao().find(id);
        if (!isUserOwnerOfDmp(dmp, principal))
            throw new Exception("User does not have the privilege to do this action.");
        if (dmp.getStatus().equals(DMP.DMPStatus.FINALISED.getValue()))
            throw new Exception("DMP is already finalized");
        List<Dataset> indexDatasets = new ArrayList<>();
        if (datasetsToBeFinalized != null && datasetsToBeFinalized.getUuids() != null && !datasetsToBeFinalized.getUuids().isEmpty()) {
            List<Dataset> finalizedDatasets = apiContext.getOperationsContext().getDatabaseRepository().getDatasetDao()
                      .asQueryable().where((builder, root) -> root.get("id").in(datasetsToBeFinalized.getUuids()))
                      .toList();
            for (Dataset dataset: finalizedDatasets) {
                Dataset.Status status = Dataset.Status.fromInteger(dataset.getStatus());
                Date finalizedDate = dataset.getFinalizedAt();
                dataset.setStatus(Dataset.Status.FINALISED.getValue());
                dataset.setFinalizedAt(new Date());
                DatasetWizardModel wizardModel = new DatasetWizardModel();
                wizardModel = wizardModel.fromDataModel(dataset);
                wizardModel.setDatasetProfileDefinition(this.datasetManager.getPagedProfile(wizardModel, dataset));
                try {
                    datasetManager.createOrUpdate(wizardModel, principal);
                } catch (Exception e) {
                    dataset.setStatus(status.getValue());
                    dataset.setFinalizedAt(finalizedDate);
                    throw e;
                }
                dataset.setModified(new Date());
            }
            /*apiContext.getOperationsContext().getDatabaseRepository().getDatasetDao()
                      .asQueryable().where((builder, root) -> root.get("id").in(datasetsToBeFinalized.getUuids()))
                      .update(root -> root.<Date>get("finalizedAt"), new Date());*/
            //List<Dataset> finalizedDatasets = dmp.getDataset().stream().filter(dataset -> datasetsToBeFinalized.getUuids().contains(dataset.getId())).collect(Collectors.toList());
/*            finalizedDatasets.forEach(dataset ->{
                dataset.setStatus(Dataset.Status.FINALISED.getValue());
                dataset.setFinalizedAt(new Date());
                dataset.setModified(new Date());
            } );*/
            indexDatasets.addAll(finalizedDatasets);
            List<UUID> datasetsToBeCanceled = new LinkedList<>();
            for (Dataset dataset : dmp.getDataset()) {
                if (!dataset.getStatus().equals(Dataset.Status.FINALISED.getValue()) && !datasetsToBeFinalized.getUuids().contains(dataset.getId())) {
                    datasetsToBeCanceled.add(dataset.getId());
                }
            }
            if (!datasetsToBeCanceled.isEmpty()) {
                apiContext.getOperationsContext().getDatabaseRepository().getDatasetDao()
                          .asQueryable().where((builder, root) -> root.get("id").in(datasetsToBeCanceled))
                          .update(root -> root.<Integer>get("status"), Dataset.Status.CANCELED.getValue());
                List<Dataset> cancelledDatasets = dmp.getDataset().stream().filter(dataset -> datasetsToBeCanceled.contains(dataset.getId())).collect(Collectors.toList());
                cancelledDatasets.forEach(dataset -> dataset.setStatus(Dataset.Status.CANCELED.getValue()));
                indexDatasets.addAll(cancelledDatasets);
            }
        } else {
            List<UUID> datasetsToBeCanceled = new LinkedList<>();
            for (Dataset dataset : dmp.getDataset()) {
                if (!dataset.getStatus().equals(Dataset.Status.FINALISED.getValue())) {
                    datasetsToBeCanceled.add(dataset.getId());
                }
            }
            if (!datasetsToBeCanceled.isEmpty()) {
                apiContext.getOperationsContext().getDatabaseRepository().getDatasetDao()
                          .asQueryable().where((builder, root) -> root.get("id").in(datasetsToBeCanceled))
                          .update(root -> root.<Integer>get("status"), Dataset.Status.CANCELED.getValue());
                List<Dataset> cancelledDatasets = dmp.getDataset().stream().filter(dataset -> datasetsToBeCanceled.contains(dataset.getId())).collect(Collectors.toList());
                cancelledDatasets.forEach(dataset -> dataset.setStatus(Dataset.Status.CANCELED.getValue()));
                indexDatasets.addAll(cancelledDatasets);
            }
        }
        dmp.setStatus(DMP.DMPStatus.FINALISED.getValue());
        apiContext.getOperationsContext().getDatabaseRepository().getDmpDao().createOrUpdate(dmp);
        UUID dmpId = dmp.getId();
        dmp.setUsers(new HashSet<>(apiContext.getOperationsContext().getDatabaseRepository().getUserDmpDao().asQueryable().where((builder, root) -> builder.equal(root.get("dmp").get("id"), dmpId)).toList()));
        this.updateIndex(dmp);
        UserInfo user = apiContext.getOperationsContext().getDatabaseRepository().getUserInfoDao().find(principal.getId());
        sendNotification(dmp, user, NotificationType.DMP_FINALISED);
        metricsManager.decreaseValue(MetricNames.DMP, 1, MetricNames.DRAFT);
        metricsManager.increaseValue(MetricNames.DMP, 1, MetricNames.FINALIZED);
        this.updateDatasetsIndex(indexDatasets);
        metricsManager.decreaseValue(MetricNames.DATASET, indexDatasets.size(), MetricNames.DRAFT);
        metricsManager.increaseValue(MetricNames.DATASET, indexDatasets.size(), MetricNames.FINALIZED);
    }

    public void undoFinalize(UUID id, Principal principal) throws Exception {
        DMP dmp = this.apiContext.getOperationsContext().getDatabaseRepository().getDmpDao().find(id);
        if (!isUserOwnerOfDmp(dmp, principal))
            throw new Exception("User does not have the privilege to do this action.");
        if (dmp.getStatus().equals(DMP.DMPStatus.ACTIVE.getValue()))
            throw new Exception("DMP is already Active");
        dmp.setStatus(DMP.DMPStatus.ACTIVE.getValue());
        apiContext.getOperationsContext().getDatabaseRepository().getDmpDao().createOrUpdate(dmp);
        UUID dmpId = dmp.getId();
        dmp.setUsers(new HashSet<>(apiContext.getOperationsContext().getDatabaseRepository().getUserDmpDao().asQueryable().where((builder, root) -> builder.equal(root.get("dmp").get("id"), dmpId)).toList()));
        this.updateIndex(dmp);
        metricsManager.decreaseValue(MetricNames.DMP, 1, MetricNames.FINALIZED);
        metricsManager.increaseValue(MetricNames.DMP, 1, MetricNames.DRAFT);
    }

    public void updateUsers(UUID id, List<UserInfoListingModel> users, Principal principal) throws Exception {
        DMP dmp = this.apiContext.getOperationsContext().getDatabaseRepository().getDmpDao().find(id);
        if (!isUserOwnerOfDmp(dmp, principal))
            throw new Exception("User does not have the privilege to do this action.");
        clearUsers(dmp);
        for (UserInfoListingModel userListing : users) {
            UserInfo tempUser = apiContext.getOperationsContext().getDatabaseRepository().getUserInfoDao().find(userListing.getId());
            assignUser(dmp, tempUser, UserDMP.UserDMPRoles.fromInteger(userListing.getRole()));
        }

    }

    /*
    * Export Data
    * */

    public FileEnvelope getWordDocument(String id, Principal principal, ConfigLoader configLoader) throws IOException {
        return this.getWordDocument(id, principal, configLoader, true);
    }

    public FileEnvelope getWordDocument(String id, Principal principal, ConfigLoader configLoader, Boolean versioned) throws IOException {
        WordBuilder wordBuilder = new WordBuilder(this.environment);
        VisibilityRuleService visibilityRuleService = new VisibilityRuleServiceImpl();
        DatasetWizardModel dataset = new DatasetWizardModel();
        XWPFDocument document = configLoader.getDocument();

        eu.eudat.data.entities.DMP dmpEntity = databaseRepository.getDmpDao().find(UUID.fromString(id));
        if (!dmpEntity.isPublic() && dmpEntity.getUsers().stream().filter(userInfo -> userInfo.getUser().getId() == principal.getId()).collect(Collectors.toList()).size() == 0)
            throw new UnauthorisedException();

        wordBuilder.fillFirstPage(dmpEntity, null, document);

//        int powered_pos = document.getParagraphs().size() - 3;
        int powered_pos = wordBuilder.findPosOfPoweredBy(document);
        XWPFParagraph powered_par = null;
        XWPFParagraph argos_img_par = null;
        if(powered_pos != -1) {
            powered_par = document.getParagraphArray(powered_pos);
            argos_img_par = document.getParagraphArray(powered_pos + 1);
        }


//        // DMP info on top of the document.
//        wordBuilder.addParagraphContent("Data Management Plan Information", document, ParagraphStyle.HEADER1, BigInteger.ZERO);
//        // DMP title custom style.
//        wordBuilder.addParagraphContent(dmpEntity.getLabel(), document, ParagraphStyle.HEADER2, BigInteger.ZERO);
//        wordBuilder.addParagraphContent(dmpEntity.getDescription(), document, ParagraphStyle.HTML, BigInteger.ZERO);
//
//        wordBuilder.addParagraphContent("Funder", document, ParagraphStyle.HEADER3, BigInteger.ZERO);
//        if (dmpEntity.getGrant().getFunder() != null)
//            wordBuilder.addParagraphContent(dmpEntity.getGrant().getFunder().getLabel(), document, ParagraphStyle.TEXT, BigInteger.ZERO);
//
//        wordBuilder.addParagraphContent("Grant", document, ParagraphStyle.HEADER3, BigInteger.ZERO);
//        wordBuilder.addParagraphContent(dmpEntity.getGrant().getLabel(), document, ParagraphStyle.TEXT, BigInteger.ZERO);
//
//        wordBuilder.addParagraphContent("Organisations", document, ParagraphStyle.HEADER3, BigInteger.ZERO);
//        if (dmpEntity.getOrganisations().size() > 0) {
//            wordBuilder.addParagraphContent(dmpEntity.getOrganisations().stream().map(Organisation::getLabel).collect(Collectors.joining(", "))
//                    , document, ParagraphStyle.TEXT, BigInteger.ZERO);
//        }
//
//        wordBuilder.addParagraphContent("Researchers", document, ParagraphStyle.HEADER3, BigInteger.ZERO);
//        if (dmpEntity.getResearchers().size() > 0) {
//            wordBuilder.addParagraphContent(dmpEntity.getResearchers().stream().map(Researcher::getLabel).collect(Collectors.joining(", "))
//                    , document, ParagraphStyle.TEXT, BigInteger.ZERO);
//        }
//
//        /*wordBuilder.addParagraphContent("DMP Profile", document, ParagraphStyle.HEADER2, BigInteger.ZERO);
//        if (dmpEntity.getProfile() != null){
//            wordBuilder.addParagraphContent(dmpEntity.getProfile().getLabel(), document, ParagraphStyle.TEXT, BigInteger.ZERO);
//        }*/
//
//        // Page break at the end of the DMP title.
//        XWPFParagraph parBreakDMP = document.createParagraph();
//        parBreakDMP.setPageBreak(true);
//
//        wordBuilder.addParagraphContent("Datasets", document, ParagraphStyle.HEADER1, BigInteger.ZERO);
//        // Space below Datasets.
//        XWPFParagraph parBreakDatasets = document.createParagraph();
        final Boolean isFinalized = dmpEntity.getStatus() == DMP.DMPStatus.FINALISED.getValue();
        final Boolean isPublic = dmpEntity.isPublic();
        dmpEntity.getDataset().stream()
                 .filter(item -> item.getStatus() != Dataset.Status.CANCELED.getValue())
                 .filter(item -> item.getStatus() != Dataset.Status.DELETED.getValue())
                .filter(item -> !isPublic && !isFinalized || item.getStatus() == Dataset.Status.FINALISED.getValue())
                .sorted(Comparator.comparing(Dataset::getCreated))
                 .forEach(datasetEntity -> {
                     Map<String, Object> properties = new HashMap<>();
                     if (datasetEntity.getProperties() != null) {
                         //ObjectMapper objectMapper = new ObjectMapper();
                         try {
                             properties = objectMapper.readValue(datasetEntity.getProperties(), LinkedHashMap.class);
                         } catch (IOException e) {
                             logger.error(e.getLocalizedMessage(), e);
                         }
                         /*JSONObject jObject = new JSONObject(datasetEntity.getProperties());
                         properties = jObject.toMap();*/
                     }

                     // Custom style for the Dataset title.
                     //wordBuilder.addParagraphContent("Title: " + datasetEntity.getLabel(), document, ParagraphStyle.HEADER1, BigInteger.ZERO);
                     XWPFParagraph datasetLabelParagraph = document.createParagraph();
//                     datasetLabelParagraph.setStyle("Heading2");
                     datasetLabelParagraph.setSpacingBetween(1.0);
                     XWPFRun runDatasetTitle1 = datasetLabelParagraph.createRun();
                     runDatasetTitle1.setText("Title: ");
                     runDatasetTitle1.setColor("000000");
                     //runDatasetTitle1.setBold(true);
                     //runDatasetTitle1.setFontSize(12);
                     XWPFRun runDatasetTitle = datasetLabelParagraph.createRun();
                     runDatasetTitle.setText(datasetEntity.getLabel());
                     runDatasetTitle.setColor("116a78");
                     //runDatasetTitle.setBold(true);
                     //runDatasetTitle.setFontSize(12);

                     XWPFParagraph datasetTemplateParagraph = document.createParagraph();
//                     datasetTemplateParagraph.setStyle("Heading3");
                     XWPFRun runDatasetTemplate1 = datasetTemplateParagraph.createRun();
                     runDatasetTemplate1.setText("Template: ");
                     runDatasetTemplate1.setColor("000000");
                     //runDatasetTemplate1.setBold(true);
                     //runDatasetTemplate1.setFontSize(12);
                     XWPFRun runDatasetTemplate = datasetTemplateParagraph.createRun();
                     runDatasetTemplate.setText(datasetEntity.getProfile().getLabel());
                     runDatasetTemplate.setColor("116a78");
                     //runDatasetTemplate.setBold(true);
                     //runDatasetTemplate.setFontSize(12);

                     document.createParagraph();

//                     /*XWPFParagraph externalReferencesParagraph = document.createParagraph();
//                     externalReferencesParagraph.setStyle("Heading3");
//                     XWPFRun externalReferencesRun = externalReferencesParagraph.createRun();
//                     externalReferencesRun.setText("External References");
//                     externalReferencesRun.setColor("2E75B6");
//                     externalReferencesRun.setBold(true);
//                     externalReferencesRun.setFontSize(12);
//
//                     wordBuilder.addParagraphContent("Data Repositories", document, ParagraphStyle.HEADER4, BigInteger.ZERO);
//                     if (datasetEntity.getDatasetDataRepositories().size() > 0) {
//                         wordBuilder.addParagraphContent(datasetEntity.getDatasetDataRepositories().stream().map(DatasetDataRepository::getDataRepository).map(DataRepository::getLabel).collect(Collectors.joining(", "))
//                                 , document, ParagraphStyle.TEXT, BigInteger.ZERO);
//                     }
//                     wordBuilder.addParagraphContent("External Datasets", document, ParagraphStyle.HEADER4, BigInteger.ZERO);
//                     if (datasetEntity.getDatasetExternalDatasets().size() > 0) {
//                         wordBuilder.addParagraphContent(datasetEntity.getDatasetExternalDatasets().stream().map(DatasetExternalDataset::getExternalDataset).map(ExternalDataset::getLabel).collect(Collectors.joining(", "))
//                                 , document, ParagraphStyle.TEXT, BigInteger.ZERO);
//                     }
//                     wordBuilder.addParagraphContent("Registries", document, ParagraphStyle.HEADER4, BigInteger.ZERO);
//                     if (datasetEntity.getRegistries().size() > 0) {
//                         wordBuilder.addParagraphContent(datasetEntity.getRegistries().stream().map(Registry::getLabel).collect(Collectors.joining(", "))
//                                 , document, ParagraphStyle.TEXT, BigInteger.ZERO);
//                     }
//                     wordBuilder.addParagraphContent("Services", document, ParagraphStyle.HEADER4, BigInteger.ZERO);
//                     if (datasetEntity.getServices().size() > 0) {
//                         wordBuilder.addParagraphContent(datasetEntity.getServices().stream().map(DatasetService::getService).map(Service::getLabel).collect(Collectors.joining(", "))
//                                 , document, ParagraphStyle.TEXT, BigInteger.ZERO);
//                     }
//                    *//*wordBuilder.addParagraphContent("Tags", document, ParagraphStyle.HEADER3, BigInteger.ZERO);
//                    if (datasetEntity.().size() > 0) {
//                        wordBuilder.addParagraphContent(datasetEntity.getServices().stream().map(DatasetService::getService).map(Service::getLabel).collect(Collectors.joining(", "))
//                                , document, ParagraphStyle.HEADER4, BigInteger.ZERO);
//                    }*/
//
//
                     wordBuilder.addParagraphContent(datasetEntity.getDescription(), document, ParagraphStyle.HTML, BigInteger.ZERO, 0);

                     // Dataset Description custom style.
                     XWPFParagraph datasetDescriptionParagraph = document.createParagraph();
                     datasetDescriptionParagraph.setStyle("Heading4");
                     datasetDescriptionParagraph.setSpacingBetween(1.5);
                     XWPFRun datasetDescriptionRun = datasetDescriptionParagraph.createRun();
                     datasetDescriptionRun.setText("Dataset Description");
                     //datasetDescriptionRun.setColor("2E75B6");
                     //datasetDescriptionRun.setBold(true);
                     datasetDescriptionRun.setFontSize(15);

                     PagedDatasetProfile pagedDatasetProfile = datasetManager.getPagedProfile(dataset, datasetEntity);
                     visibilityRuleService.setProperties(properties);
                     visibilityRuleService.buildVisibilityContext(pagedDatasetProfile.getRules());
                     try {
                         wordBuilder.build(document, pagedDatasetProfile, visibilityRuleService);
                     } catch (IOException e) {
                         logger.error(e.getMessage(), e);
                     }
                     // Page break at the end of the Dataset.
                     XWPFParagraph parBreakDataset = document.createParagraph();
                     parBreakDataset.setPageBreak(true);
                 });

//        // Removes the top empty headings.
//        for (int i = 0; i < 6; i++) {
//            document.removeBodyElement(0);
//        }


        if(powered_pos != -1) {
            document.getLastParagraph().setPageBreak(false);
            document.createParagraph();
            document.setParagraph(powered_par, document.getParagraphs().size() - 1);

            document.createParagraph();
            document.setParagraph(argos_img_par, document.getParagraphs().size() - 1);

            document.removeBodyElement(powered_pos + 1);
            document.removeBodyElement(powered_pos + 1);
        }

        wordBuilder.fillFooter(dmpEntity, null, document, false);

        String fileName = "DMP_" + dmpEntity.getGrant().getLabel();
        if (versioned) {
            fileName += "_" + dmpEntity.getVersion();
        }
       // fileName = fileName.replaceAll("[^a-zA-Z0-9+ ]", "");
        FileEnvelope exportEnvelope = new FileEnvelope();
        exportEnvelope.setFilename(fileName + ".docx");
        String uuid = UUID.randomUUID().toString();
        File exportFile = new File(this.environment.getProperty("temp.temp") + uuid + ".docx");
        FileOutputStream out = new FileOutputStream(exportFile);
        document.write(out);
        out.close();
        exportEnvelope.setFile(exportFile);

        return exportEnvelope;
    }

    private FileEnvelope getXmlDocument(String id, Principal principal) throws InstantiationException, IllegalAccessException, IOException {
        ExportXmlBuilder xmlBuilder = new ExportXmlBuilder();
        VisibilityRuleService visibilityRuleService = new VisibilityRuleServiceImpl();
        eu.eudat.data.entities.DMP dmp = databaseRepository.getDmpDao().find(UUID.fromString(id));
        if (!dmp.isPublic() && dmp.getUsers().stream().filter(userInfo -> userInfo.getUser().getId() == principal.getId()).collect(Collectors.toList()).size() == 0)
            throw new UnauthorisedException();
        final Boolean isFinalized = dmp.getStatus() == DMP.DMPStatus.FINALISED.getValue();
        final Boolean isPublic = dmp.isPublic();
        List<Dataset> datasets = dmp.getDataset().stream()
                .filter(dataset -> dataset.getStatus() != Dataset.Status.DELETED.getValue() &&
                        dataset.getStatus() != Dataset.Status.CANCELED.getValue())
                .filter(dataset -> !isPublic && !isFinalized || dataset.getStatus() == Dataset.Status.FINALISED.getValue())
                .collect(Collectors.toList());
        /*String fileName = dmp.getLabel();
        fileName = fileName.replaceAll("[^a-zA-Z0-9+ ]", "");*/
        String uuid = UUID.randomUUID().toString();
        File xmlFile = new File(this.environment.getProperty("temp.temp") + uuid + ".xml");
        BufferedWriter writer = new BufferedWriter(new FileWriter(xmlFile, true));
        Document xmlDoc = XmlBuilder.getDocument();
        Element dmpElement = xmlDoc.createElement("dmp");
        Element dmpDescription = xmlDoc.createElement("description");
        dmpDescription.setTextContent(dmp.getDescription());
        dmpElement.appendChild(dmpDescription);
        Element dmpName = xmlDoc.createElement("dmpName");
        dmpName.setTextContent(dmp.getLabel());
        dmpElement.appendChild(dmpName);

        if (dmp.getExtraProperties() != null && !dmp.getExtraProperties().isEmpty()) {
            Map<String, Object> extraProperties = new ObjectMapper().readValue(dmp.getExtraProperties(), HashMap.class);
            Element language = xmlDoc.createElement("language");
            language.setTextContent(extraProperties.get("language") != null ? extraProperties.get("language").toString() : null);
            dmpElement.appendChild(language);
            Element visibility = xmlDoc.createElement("visibility");
            visibility.setTextContent(extraProperties.get("visible") != null ? extraProperties.get("visible").toString() : null);
            dmpElement.appendChild(visibility);
            Element publicDate = xmlDoc.createElement("publicDate");
            publicDate.setTextContent(extraProperties.get("publicDate") != null ? extraProperties.get("publicDate").toString() : null);
            dmpElement.appendChild(publicDate);
            Element costs = xmlDoc.createElement("costs");
            costs.setTextContent(extraProperties.get("costs") != null ? extraProperties.get("costs").toString() : null);
            dmpElement.appendChild(costs);
        }

        DMPProfile dmpProfile = dmp.getProfile();
        Element dmpProfileElement = xmlDoc.createElement("dmpProfile");
        Element dmpProfileName = xmlDoc.createElement("dmpProfileName");
        if (!(dmpProfile == null)) {
            dmpProfileName.setTextContent(dmpProfile.getLabel());
            dmpProfileElement.appendChild(dmpProfileName);
            Element dmpProfileId = xmlDoc.createElement("dmpProfileId");
            dmpProfileId.setTextContent(dmpProfile.getId().toString());
            dmpProfileElement.appendChild(dmpProfileId);
            Element values = xmlDoc.createElement("values");
            values.setTextContent(dmpProfile.getDefinition());
            dmpProfileElement.appendChild(values);
        }
        dmpElement.appendChild(dmpProfileElement);

        // Funder.
        Element funder = xmlDoc.createElement("funder");
        Element funderLabel = xmlDoc.createElement("label");
        Element funderId = xmlDoc.createElement("id");
        funderLabel.setTextContent(dmp.getGrant().getFunder().getLabel());
        funderId.setTextContent(dmp.getGrant().getFunder().getId().toString());
        funder.appendChild(funderLabel);
        funder.appendChild(funderId);
        dmpElement.appendChild(funder);
        // Grant.
        Element grant = xmlDoc.createElement("grant");
        Element grantLabel = xmlDoc.createElement("label");
        Element grantId = xmlDoc.createElement("id");
        grantLabel.setTextContent(dmp.getGrant().getLabel());
        grantId.setTextContent(dmp.getGrant().getId().toString());
        grant.appendChild(grantLabel);
        grant.appendChild(grantId);
        dmpElement.appendChild(grant);
        // Project.
        Element project = xmlDoc.createElement("project");
        Element projectLabel = xmlDoc.createElement("label");
        Element projectId = xmlDoc.createElement("id");
        projectLabel.setTextContent(dmp.getProject().getLabel());
        projectId.setTextContent(dmp.getProject().getId().toString());
        project.appendChild(projectLabel);
        project.appendChild(projectId);
        dmpElement.appendChild(project);

        Element organisationsElement = xmlDoc.createElement("organisations");
        for (Organisation organisation : dmp.getOrganisations()) {
            Element organisationElement = xmlDoc.createElement("organisation");
            Element organisationNameElement = xmlDoc.createElement("name");
            organisationNameElement.setTextContent(organisation.getLabel());
            Element organisationReferenceElement = xmlDoc.createElement("reference");
            organisationReferenceElement.setTextContent(organisation.getReference());
            organisationElement.appendChild(organisationNameElement);
            organisationElement.appendChild(organisationReferenceElement);
            organisationsElement.appendChild(organisationElement);
        }
        dmpElement.appendChild(organisationsElement);

        Element researchersElement = xmlDoc.createElement("researchers");
        for (Researcher researcher : dmp.getResearchers()) {
            Element researcherElement = xmlDoc.createElement("researcher");
            Element researcherNameElement = xmlDoc.createElement("name");
            researcherNameElement.setTextContent(researcher.getLabel());
            Element researcherReferenceElement = xmlDoc.createElement("reference");
            researcherReferenceElement.setTextContent(researcher.getReference());
            researcherElement.appendChild(researcherNameElement);
            researcherElement.appendChild(researcherReferenceElement);
            researchersElement.appendChild(researcherElement);
        }
        dmpElement.appendChild(researchersElement);
        Element datasetsElement = xmlDoc.createElement("datasets");

        for (Dataset dataset : datasets) {
            Element datasetElement = xmlDoc.createElement("dataset");
            Element datsetProfileElement = xmlDoc.createElement("profile");
            datasetElement.setAttribute("name", dataset.getLabel());
            datasetElement.appendChild(datsetProfileElement);
            datsetProfileElement.setTextContent(dataset.getProfile().getId().toString());

            DatasetWizardModel datasetWizardModel = new DatasetWizardModel();
            Map<String, Object> properties = new HashMap<>();
            if (dataset.getProperties() != null) {
                JSONObject jobject = new JSONObject(dataset.getProperties());
                properties = jobject.toMap();
            }
            PagedDatasetProfile pagedDatasetProfile = datasetManager.getPagedProfile(datasetWizardModel, dataset);
            visibilityRuleService.setProperties(properties);
            visibilityRuleService.buildVisibilityContext(pagedDatasetProfile.getRules());
            datasetElement.appendChild(xmlBuilder.createPages(pagedDatasetProfile.getPages(), visibilityRuleService, xmlDoc));
            datasetsElement.appendChild(datasetElement);
        }
        Element profiles = xmlDoc.createElement("profiles");

        // Get DatasetProfiles from DMP to add to XML.
        for (DatasetProfile datasetProfile : dmp.getAssociatedDmps()) {
            Element profile = xmlDoc.createElement("profile");
            Element profileLabel = xmlDoc.createElement("profilelabel");
            profileLabel.setTextContent(datasetProfile.getLabel());
            profile.appendChild(profileLabel);
            Element profileId = xmlDoc.createElement("profileId");
            profileId.setTextContent(datasetProfile.getId().toString());
            profile.appendChild(profileId);
            profiles.appendChild(profile);
        }
        dmpElement.appendChild(profiles);
        dmpElement.appendChild(datasetsElement);
        xmlDoc.appendChild(dmpElement);
        String xml = XmlBuilder.generateXml(xmlDoc);
        writer.write(xml);
        writer.close();
        FileEnvelope fileEnvelope = new FileEnvelope();
        fileEnvelope.setFile(xmlFile);
        fileEnvelope.setFilename("DMP_" + dmp.getGrant().getLabel() + "_" + dmp.getVersion() + ".xml");

        return fileEnvelope;
    }

    public ResponseEntity<byte[]> getRDAJsonDocument(String id, Principal principal) throws Exception {
        eu.eudat.data.entities.DMP dmp = databaseRepository.getDmpDao().find(UUID.fromString(id));
        if (!dmp.isPublic() && dmp.getUsers().stream().noneMatch(userInfo -> userInfo.getUser().getId() == principal.getId()))
            throw new UnauthorisedException();
//        RDAExportModel rdaExportModel = new RDAExportModel().fromDataModel(dmp, datasetManager, principal);
        final Boolean isFinalized = dmp.getStatus() == DMP.DMPStatus.FINALISED.getValue();
        final Boolean isPublic = dmp.isPublic();
        dmp.setDataset(dmp.getDataset().stream()
                .filter(dataset -> dataset.getStatus() != Dataset.Status.DELETED.getValue() &&
                        dataset.getStatus() != Dataset.Status.CANCELED.getValue())
                .filter(dataset -> !isPublic && !isFinalized || dataset.getStatus() == Dataset.Status.FINALISED.getValue())
                .collect(Collectors.toSet()));
        String result = rdaManager.convertToRDA(dmp);

        /*ObjectMapper mapper = new ObjectMapper();
        mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);*/
        String fileName = "DMP_" + dmp.getGrant().getLabel() + "_" + dmp.getVersion();//dmp.getLabel();
        fileName = fileName.replaceAll("[^a-zA-Z0-9+ ]", "").replace(" ", "_").replace(",", "_");
        String uuid = UUID.randomUUID().toString();
        File file = new File(this.environment.getProperty("temp.temp") + uuid + ".json");
        OutputStream output = new FileOutputStream(file);
        try {
//            mapper.writeValue(file, rdaExportModel);
            output.write(result.getBytes());
            output.flush();
            output.close();
        } catch (IOException e) {
            logger.error(e.getMessage(), e);
        }

        InputStream resource = new FileInputStream(file);
        HttpHeaders responseHeaders = new HttpHeaders();
        responseHeaders.setContentLength(file.length());
        responseHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM);
        responseHeaders.set("Content-Disposition", "attachment;filename=" + fileName + ".json");
        responseHeaders.set("Access-Control-Expose-Headers", "Content-Disposition");
        responseHeaders.get("Access-Control-Expose-Headers").add("Content-Type");

        byte[] content = org.apache.poi.util.IOUtils.toByteArray(resource);
        resource.close();
        Files.deleteIfExists(file.toPath());
        return new ResponseEntity<>(content, responseHeaders, HttpStatus.OK);
    }

    public ResponseEntity<byte[]> getDocument(String id, String contentType, Principal principal, ConfigLoader configLoader) throws InstantiationException, IllegalAccessException, IOException {
        FileEnvelope file;
        switch (contentType) {
            case "application/xml":
                file = getXmlDocument(id, principal);
                break;
            case "application/msword":
                file = getWordDocument(id, principal, configLoader);
                break;
            default:
                file = getXmlDocument(id, principal);
        }
        String fileName = file.getFilename().replace(" ", "_").replace(",", "_");
        InputStream resource = new FileInputStream(file.getFile());
        HttpHeaders responseHeaders = new HttpHeaders();
        responseHeaders.setContentLength(file.getFile().length());
        responseHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM);
        responseHeaders.set("Content-Disposition", "attachment;filename=" + fileName);
        responseHeaders.set("Access-Control-Expose-Headers", "Content-Disposition");
        responseHeaders.get("Access-Control-Expose-Headers").add("Content-Type");

        byte[] content = org.apache.poi.util.IOUtils.toByteArray(resource);
        resource.close();
        Files.deleteIfExists(file.getFile().toPath());
        return new ResponseEntity<>(content,
                responseHeaders,
                HttpStatus.OK);
    }

    /*
    * Data Import
    * */

    public List<DmpImportModel> createDmpFromXml(MultipartFile[] files, Principal principal, String[] profiles) throws IOException, JAXBException, Exception {
        List<DmpImportModel> dataManagementPlans = new ArrayList<>();
        // Jaxb approach.
        JAXBContext jaxbContext;

        for (MultipartFile multipartFile : Arrays.asList(files)) {  // Gets one item from the array.
            try {
                InputStream in = multipartFile.getInputStream();  // Transforms item to InputStream.
                jaxbContext = JAXBContext.newInstance(DmpImportModel.class);
                Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
                DmpImportModel dmpImportModel = (DmpImportModel) jaxbUnmarshaller.unmarshal(in);
                dataManagementPlans.add(dmpImportModel);
            } catch (IOException | JAXBException ex) {
                logger.error(ex.getMessage(), ex);
            }
            // TODO Iterate through the list of dataManagementPlans.
            // Creates new dataManagementPlan to fill it with the data model that was parsed from the xml.
            // Creates properties.
            DataManagementPlanEditorModel dm = new DataManagementPlanEditorModel();
            DataManagementPlanProfile dmpProfile = new DataManagementPlanProfile();

            List<Field> fieldList = new LinkedList<>();
            Field field = new Field();
            field.setLabel(dataManagementPlans.get(0).getDmpProfile().getDmpProfileName());
            field.setId(dataManagementPlans.get(0).getDmpProfile().getDmpProfileId());

            fieldList.add(field);
            dmpProfile.setFields(fieldList);

            /*Tuple tuple = new Tuple();
            tuple.setId(dataManagementPlans.get(0).getDmpProfile().getDmpProfileId());
            tuple.setLabel(dataManagementPlans.get(0).getDmpProfile().getDmpProfileName());*/
            eu.eudat.models.data.funder.Funder funder = new eu.eudat.models.data.funder.Funder();
            FunderImportModels funderImport = dataManagementPlans.get(0).getFunderImportModels();
            funder.setId(funderImport.getId());
            funder.setLabel(funderImport.getLabel());
            FunderDMPEditorModel funderEditor = new FunderDMPEditorModel();
            funderEditor.setExistFunder(funder);

            eu.eudat.models.data.grant.Grant grant = new eu.eudat.models.data.grant.Grant();
            GrantImportModels grantImport = dataManagementPlans.get(0).getGrantImport();
            grant.setId(grantImport.getId());
            grant.setLabel(grantImport.getLabel());
            grant.setAbbreviation(grantImport.getAbbreviation());
            grant.setDescription(grantImport.getDescription());
            GrantDMPEditorModel grantEditor = new GrantDMPEditorModel();
            grantEditor.setExistGrant(grant);

            eu.eudat.models.data.project.Project project = new eu.eudat.models.data.project.Project();
            ProjectImportModels projectImport = dataManagementPlans.get(0).getProjectImportModels();
            project.setId(projectImport.getId());
            project.setLabel(projectImport.getLabel());
            ProjectDMPEditorModel projectEditor = new ProjectDMPEditorModel();
            projectEditor.setExistProject(project);

            List<eu.eudat.models.data.dmp.AssociatedProfile> associatedProfiles = new LinkedList<>();
            if (profiles != null && profiles.length > 0) {
                for (String profile : profiles) {
                    DatasetProfile exProfile = apiContext.getOperationsContext().getDatabaseRepository().getDatasetProfileDao().find(UUID.fromString(profile));
                    AssociatedProfile associatedProfile = new AssociatedProfile().fromData(exProfile);
                    associatedProfiles.add(associatedProfile);
                }
            }

            for (AssociatedProfileImportModels a : dataManagementPlans.get(0).getProfilesImportModels()) {
                try {
                    DatasetProfile exProfile = apiContext.getOperationsContext().getDatabaseRepository().getDatasetProfileDao().find(a.getId());
                    AssociatedProfile associatedProfile = new AssociatedProfile().fromData(exProfile);
                    associatedProfiles.add(associatedProfile);
                } catch (Exception ignored) {
                }
            }
            List<eu.eudat.models.data.dmp.Organisation> organisations = new ArrayList<>();
            for (OrganisationImportModel org : dataManagementPlans.get(0).getOrganisationImportModels()) {
                eu.eudat.models.data.dmp.Organisation organisation = new eu.eudat.models.data.dmp.Organisation();
                organisation.setName(org.getOrganaisationNameImport());
                organisation.setReference(org.getOrganaisationReferenceImport());
                organisation.setKey(organisation.getReference().split(":")[0]);
                organisations.add(organisation);
            }
            List<eu.eudat.models.data.dmp.Researcher> researchers = new LinkedList<>();
            for (ResearcherImportModels res : dataManagementPlans.get(0).getResearchersImportModels()) {
                eu.eudat.models.data.dmp.Researcher researcher = new eu.eudat.models.data.dmp.Researcher();
                researcher.setLabel(res.getResearcherImportName());
                researcher.setName(res.getResearcherImportName());
                researcher.setReference(res.getResearcherImportReference());
                researcher.setKey(researcher.getReference().split(":")[0]);
                researchers.add(researcher);
            }

            List<UserListingModel> associatedUsers = new LinkedList<>();
            List<DynamicFieldWithValue> dynamicFields = new LinkedList<>();

            // Sets properties.
            dm.setLabel(files[0].getOriginalFilename());  // Sets label.
            dm.setGrant(grantEditor);  //Sets grant property.
            dm.setFunder(funderEditor);
            dm.setProject(projectEditor);
            dm.setDescription(dataManagementPlans.get(0).getDescriptionImport()); // Sets description property.
            dm.setProfiles(associatedProfiles);
            dm.setOrganisations(organisations);  // Sets organisations property.
            dm.setResearchers(researchers);  // Sets researchers property.
            dm.setAssociatedUsers(associatedUsers);  // Sets associatedUsers property.
            dm.setDynamicFields(dynamicFields);  // Sets dynamicFields property.
            dm.setDefinition(dmpProfile);
            //ObjectMapper mapper = new ObjectMapper();
            Map<String, Object> extraPropertiesMap = new HashMap<>();
            if (dataManagementPlans.get(0).getLanguage() != null) {
                extraPropertiesMap.put("language", dataManagementPlans.get(0).getLanguage());
            }
            if (dataManagementPlans.get(0).getVisibility() != null) {
                extraPropertiesMap.put("visible", dataManagementPlans.get(0).getVisibility());
            }
            if (dataManagementPlans.get(0).getPublicDate() != null) {
                extraPropertiesMap.put("publicDate", dataManagementPlans.get(0).getPublicDate());
            }
            if (dataManagementPlans.get(0).getCosts() != null && !dataManagementPlans.get(0).getCosts().isEmpty()) {
                extraPropertiesMap.put("costs", objectMapper.readValue(dataManagementPlans.get(0).getCosts(), ArrayList.class));
            }
            dm.setExtraProperties(extraPropertiesMap);

            //createOrUpdate(apiContext, dm, principal);
            DMP dmp = this.createOrUpdate(dm, principal);
            if (dmp.getOrganisations() == null) {
                dmp.setOrganisations(new HashSet<>());
            }
            if (dmp.getResearchers() == null) {
                dmp.setResearchers(new HashSet<>());
            }
            if (dmp.getDataset() == null) {
                dmp.setDataset(new HashSet<>());
            }
            if (dmp.getUsers() == null) {
                dmp.setUsers(new HashSet<>());
            }
            if (dmp.getAssociatedDmps() == null) {
                dmp.setAssociatedDmps(new HashSet<>());
            }

            List<DatasetListingModel> datasets = new LinkedList<>();
            for (DatasetImportModels das: dataManagementPlans.get(0).getDatasetImportModels()) {
                eu.eudat.data.entities.Dataset dataset = new eu.eudat.data.entities.Dataset();
                dataset.setLabel(das.getName());
                try {
                    dataset.setProfile(databaseRepository.getDatasetProfileDao().find(das.getProfile()));
                } catch (Exception ignored) {
                    dataset.setProfile(databaseRepository.getDatasetProfileDao().find(associatedProfiles.get(0).getId()));
                }
                dataset.setProperties(objectMapper.writeValueAsString(das.getFieldImportModels()));
                dataset.setStatus((short) 0);
                dataset.setRegistries(new HashSet<>());
                dataset.setDatasetDataRepositories(new HashSet<>());
                dataset.setServices(new HashSet<>());
                dataset.setDatasetExternalDatasets(new HashSet<>());
                dataset.setDmp(dmp);
                DatasetWizardModel datasetWizard = new DatasetWizardModel();
                datasetWizard.setDatasetProfileDefinition(this.datasetManager.getPagedProfile(datasetWizard, dataset));
                datasetWizard.fromDataModel(dataset);
                this.datasetManager.createOrUpdate(datasetWizard, principal);
                // datasets.add(new DatasetListingModel().fromDataModel(dataset));
            }
        }

        return dataManagementPlans;
    }

    public List<DMP> createFromRDA(MultipartFile[] files, Principal principal, String[] profiles) throws IOException {
        if (principal.getId() == null) {
            throw new UnauthorisedException("No user is logged in");
        }
        List<DMP> result = new ArrayList<>();
        for (MultipartFile file: files) {
            DMP dmp = rdaManager.convertToEntity(new String(file.getBytes(), "UTF-8"), profiles);
            dmp.setLabel(file.getOriginalFilename());
            UserInfo me = apiContext.getOperationsContext().getDatabaseRepository().getUserInfoDao().find(principal.getId());
            dmp.setModified(new Date());
            dmp.setCreator(me);
            Map<String, String> extraProperties = objectMapper.readValue(dmp.getExtraProperties(), HashMap.class);
            extraProperties.put("contact", me.getId().toString());
            dmp.setExtraProperties(objectMapper.writeValueAsString(extraProperties));
            dmp.setVersion(0);
            dmp.setStatus((short)0);
            dmp.setGroupId(UUID.randomUUID());
            if (dmp.getResearchers() != null && !dmp.getResearchers().isEmpty()) {
                dmp.getResearchers().stream().filter(Objects::nonNull).forEach(researcher -> {
                    researcher.setId(UUID.randomUUID());
                    researcher.setCreated(new Date());
                    researcher.setModified(new Date());
                    researcher.setStatus((short) 0);
                    apiContext.getOperationsContext().getDatabaseRepository().getResearcherDao().createOrUpdate(researcher);
                });
            }
            databaseRepository.getDmpDao().createOrUpdate(dmp);
            assignUser(dmp, me);
            if (this.apiContext.getOperationsContext().getElasticRepository().getDmpRepository().getClient() != null) {
                UUID dmpId = dmp.getId();
                dmp.setUsers(new HashSet<>(apiContext.getOperationsContext().getDatabaseRepository().getUserDmpDao().asQueryable().where((builder, root) -> builder.equal(root.get("dmp").get("id"), dmpId)).toList()));
                this.updateIndex(dmp);
            }
            dmp.getDataset().forEach(dataset -> {
                dataset.setStatus(Dataset.Status.SAVED.getValue());
                dataset.setCreated(new Date());
                dataset.setModified(new Date());
                dataset.setDmp(dmp);
                dataset = databaseRepository.getDatasetDao().createOrUpdate(dataset);
                dataset.setRegistries(new HashSet<>());
                dataset.setDatasetDataRepositories(new HashSet<>());
                dataset.setDatasetExternalDatasets(new HashSet<>());
                dataset.setServices(new HashSet<>());
                if (dmp.getOrganisations() == null) {
                    dmp.setOrganisations(new HashSet<>());
                }
                if (dmp.getResearchers() == null) {
                    dmp.setResearchers(new HashSet<>());
                }
                dmp.setUsers(new HashSet<>(apiContext.getOperationsContext().getDatabaseRepository().getUserDmpDao().asQueryable().where((builder, root) -> builder.equal(root.get("dmp").get("id"), dmp.getId())).toList()));
                try {
                    List<Tag> tags = new ArrayList<>();
                    eu.eudat.elastic.entities.Dataset elastic = apiContext.getOperationsContext().getElasticRepository().getDatasetRepository().findDocument(dataset.getId().toString());
                    DatasetWizardModel datasetWizardModel = new DatasetWizardModel().fromDataModel(dataset);
                    if (elastic != null) {
                        tags = elastic.getTags();
                        datasetWizardModel.setTags(tags);
                    }
                    datasetManager.getTagsFromProfile(datasetWizardModel, dataset);
                    datasetManager.updateTags(dataset, datasetWizardModel.getTags());
                } catch (Exception e) {
                    logger.error(e.getMessage(), e);
                }
            });
            result.add(dmp);
        }

        return result;
    }

    /*
    * Data assignments
    * */

    private void assignUser(DMP dmp, UserInfo userInfo) {
        this.assignUser(dmp, userInfo, UserDMP.UserDMPRoles.OWNER);
    }

    private void assignUser(DMP dmp, UserInfo userInfo, UserDMP.UserDMPRoles role) {
        UserDMP userDMP = new UserDMP();
        userDMP.setDmp(dmp);
        userDMP.setUser(userInfo);
        userDMP.setRole(role.getValue());
        databaseRepository.getUserDmpDao().createOrUpdate(userDMP);
    }

    private void clearUsers(DMP dmp) {
        List<UserDMP> userDMPs = apiContext.getOperationsContext().getDatabaseRepository().getUserDmpDao().asQueryable().where(((builder, root) -> builder.equal(root.get("dmp").get("id"), dmp.getId()))).toList();
        userDMPs.forEach(userDMP -> apiContext.getOperationsContext().getDatabaseRepository().getUserDmpDao().delete(userDMP));
    }

    private void assignGrandUserIfInternal(DMP dmp, UserInfo user) {
        if (dmp.getGrant().getCreationUser() == null && (dmp.getGrant().getReference() != null && dmp.getGrant().getReference().startsWith("dmp:"))) {
            dmp.getGrant().setCreationUser(user);
        }
    }

    private void assignFunderUserIfInternal(DMP dmp, UserInfo user) {
        if (dmp.getGrant().getFunder().getCreationUser() == null && ( dmp.getGrant().getFunder().getReference() != null && dmp.getGrant().getFunder().getReference().startsWith("dmp:"))) {
            dmp.getGrant().getFunder().setCreationUser(user);
        }
    }

    private void assignProjectUserIfInternal(DMP dmp, UserInfo user) {
        if (dmp.getProject().getCreationUser() == null && (dmp.getProject().getReference() != null && dmp.getProject().getReference().startsWith("dmp:"))) {
            dmp.getProject().setCreationUser(user);
        }
    }

    /*
    * Data Index
    * */

    private void updateGroupIndex(UUID groupId) {
        DataManagementPlanCriteria criteria = new DataManagementPlanCriteria();
        criteria.setGroupIds(Collections.singletonList(groupId));
        criteria.setAllVersions(true);
        List<DMP> dmps = databaseRepository.getDmpDao().getWithCriteria(criteria).toList();
        for (DMP dmp: dmps) {
            try {
                if (dmp.getUsers() == null) {
                    UUID dmpId = dmp.getId();
                    dmp.setUsers(new HashSet<>(apiContext.getOperationsContext().getDatabaseRepository().getUserDmpDao().asQueryable().where((builder, root) -> builder.equal(root.get("dmp").get("id"), dmpId)).toList()));
                }
                this.updateIndex(dmp);
            } catch (IOException e) {
                logger.error(e.getMessage(), e);
            }
        }
    }

    private void updateDatasetsIndex(List<Dataset> datasets) {
        datasets.forEach(dataset -> {
            List<Tag> tags = new ArrayList<>();
            eu.eudat.elastic.entities.Dataset elastic = null;
            try {
                elastic = apiContext.getOperationsContext().getElasticRepository().getDatasetRepository().findDocument(dataset.getId().toString());
                if (elastic != null) {
                    tags = elastic.getTags();
                }
                UUID dmpId = dataset.getDmp().getId();
                dataset.getDmp().setUsers(new HashSet<>(apiContext.getOperationsContext().getDatabaseRepository().getUserDmpDao().asQueryable().where((builder, root) -> builder.equal(root.get("dmp").get("id"), dmpId)).toList()));
                this.datasetManager.updateTags(dataset, tags);
            } catch (Exception e) {
                logger.error(e.getMessage(), e);
            }

        });
    }

    public void updateIndex(DMP dmp) throws IOException {
        DmpMapper mapper = new DmpMapper(apiContext, datasetManager);
        Dmp elastic = mapper.toElastic(dmp);
        apiContext.getOperationsContext().getElasticRepository().getDmpRepository().createOrUpdate(elastic);
    }

    public void generateIndex(Principal principal) {
        if (principal.getAuthorities().contains(Authorities.ADMIN.getValue())) {
            if (apiContext.getOperationsContext().getElasticRepository().getDmpRepository().createIndex()) {
                List<DMP> dmps = apiContext.getOperationsContext().getDatabaseRepository().getDmpDao().asQueryable().toList();
                dmps.forEach(dmp -> {
                    try {
                        UUID dmpId = dmp.getId();
                        dmp.setUsers(new HashSet<>(apiContext.getOperationsContext().getDatabaseRepository().getUserDmpDao().asQueryable().where((builder, root) -> builder.equal(root.get("dmp").get("id"), dmpId)).toList()));
                        this.updateIndex(dmp);
                    } catch (IOException e) {
                        logger.error(e.getMessage(), e);
                    }
                });
            }
        }
    }

    public void clearIndex(Principal principal) throws IOException {
        if (principal.getAuthorities().contains(Authorities.ADMIN.getValue())) {
            apiContext.getOperationsContext().getElasticRepository().getDmpRepository().clear();
        }
    }

    /*
    * Data Checkup
    * */

    private void checkIfUserCanEditGrant(DMP dmp, UserInfo user) throws Exception{
        if (dmp.getGrant().getId() != null) {
            Grant grant = apiContext.getOperationsContext().getDatabaseRepository().getGrantDao().find(dmp.getGrant().getId());
            if (grant.getFunder() != null && dmp.getGrant().getFunder() != null
                    && !grant.getFunder().getId().equals(dmp.getGrant().getFunder().getId())) {
                if (grant.getCreationUser() == null) {
                    throw new Exception("Grant has no user, therefore, cannot be edited.");
                }
                if (!grant.getCreationUser().getId().equals(user.getId())) {
                    throw new Exception("User is not the owner of the Grant, therefore, cannot edit it.");
                }
            }
        }
    }

    private void checkDmpValidationRules(DMP dmp) throws Exception {
        if (dmp.getLabel() == null || dmp.getLabel().trim().isEmpty()) {
            throw new Exception("DMP has no label.");
        }
        if (dmp.getAssociatedDmps().size() == 0) {
            throw new Exception("DMP does not contain Dataset Templates.");
        }
        if (dmp.getGrant() == null) {
            throw new Exception("DMP has no Grant assigned.");
        }
    }

    private boolean isUserOwnerOfDmp(DMP dmp, Principal principal) {
        return (dmp.getUsers().stream().filter(userDMP -> userDMP.getRole().equals(UserDMP.UserDMPRoles.OWNER.getValue())).map(userDMP -> userDMP.getUser().getId())).collect(Collectors.toList()).contains(principal.getId());
    }

    /*
    * DOI Generation
    * */

    private String getPreviousDOI(UUID groupId, UUID selfId, String repositoryId) {
        DataManagementPlanCriteria criteria = new DataManagementPlanCriteria();
        List<UUID> groupIds = new ArrayList<>();
        groupIds.add(groupId);
        criteria.setGroupIds(groupIds);
        criteria.setAllVersions(true);
        List<DMP> dmps = this.databaseRepository.getDmpDao().getWithCriteria(criteria).toList();
        dmps.sort((DMP d1, DMP d2) -> d2.getVersion() - d1.getVersion());
        for (DMP dmp: dmps) {
            if (!dmp.getId().equals(selfId)) {
                if (dmp.getDois() != null && !dmp.getDois().isEmpty()) {
                    for (Iterator<EntityDoi> it = dmp.getDois().iterator(); it.hasNext(); ) {
                        EntityDoi entityDoi = it.next();
                        if(entityDoi.getRepositoryId().equals(repositoryId)){
                            return entityDoi.getDoi();
                        }
                    }
                }
            }
        }

        return null;
    }

    private String getUnpublishedDOI(String DOI, String token, Integer version) {
        try {
            RestTemplate restTemplate = new RestTemplate();
            HttpHeaders headers = new HttpHeaders();
            headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
            headers.setContentType(MediaType.APPLICATION_JSON);
            Map createResponse = null;
            LinkedHashMap<String, String> links = null;
            LinkedHashMap<String, String> metadata = null;
            String listUrl = this.environment.getProperty("zenodo.url") + "deposit/depositions" + "?q=conceptdoi:\"" + DOI + "\"&access_token=" + token;
            ResponseEntity<Map[]> listResponses = restTemplate.getForEntity(listUrl, Map[].class);
            createResponse = listResponses.getBody()[0];
            metadata = (LinkedHashMap<String, String>) createResponse.get("metadata");
            links = (LinkedHashMap<String, String>) createResponse.get("links");

            if (metadata.get("version").equals(version.toString())) {
                return links.get("publish");
            } else {
                return null;
            }
        }catch (Exception e) {
            logger.warn(e.getMessage(), e);
            return null;
        }
    }

    public String createZenodoDoi(UUID id, Principal principal) throws Exception {
        return this.createZenodoDoi(id, principal, false);
    }

    public String createZenodoDoi(UUID id, Principal principal, boolean update) throws Exception {
        DMP dmp = this.apiContext.getOperationsContext().getDatabaseRepository().getDmpDao().find(id);
        if (!isUserOwnerOfDmp(dmp, principal))
            throw new Exception("User is not authorized to invoke this action");
        if (!dmp.getStatus().equals(DMP.DMPStatus.FINALISED.getValue()))
            throw new Exception("DMP is not finalized");
        /*if (dmp.getDoi() != null)
            throw new Exception("DMP already has a DOI");*/

        FileEnvelope file = getWordDocument(id.toString(), principal, configLoader);
        String name = file.getFilename().substring(0, file.getFilename().length() - 5);
        File pdfFile = PDFUtils.convertToPDF(file, environment);
        String fileName = name + ".pdf";
        ResponseEntity<byte[]> jsonFile;
        try {
            jsonFile = getRDAJsonDocument(id.toString(), principal);
        } catch (Exception e) {
            throw e;
        }
        String previousDOI = this.getPreviousDOI(dmp.getGroupId(), dmp.getId(), "Zenodo");

        DMPDepositModel dmpDepositModel = DMPToDepositMapper.fromDMP(dmp, pdfFile, fileName, jsonFile, previousDOI);

        String zenodoToken = "";
        try {
            if (this.userManager.isDOITokenValid(principal)) {
                zenodoToken = principal.getZenodoToken();
            }
        } catch (NonValidTokenException e) {
            zenodoToken = this.environment.getProperty("zenodo.access_token");
        }

        String finalDoi = null;
        for(RepositoryDeposit repo: this.repositoriesDeposit) { //temp
            if(repo.getConfiguration().getRepositoryId().equals("Zenodo")) {
                finalDoi = repo.deposit(dmpDepositModel, update, zenodoToken);
                if (finalDoi != null) {
                    EntityDoi doiEntity = new EntityDoi();
                    doiEntity.setId(UUID.randomUUID());
                    doiEntity.setEntityType(EntityDoi.EntityType.DMP);
                    doiEntity.setDoi(finalDoi);
                    doiEntity.setRepositoryId("Zenodo");
                    Date now = new Date();
                    doiEntity.setCreatedAt(now);
                    doiEntity.setUpdatedAt(now);
                    doiEntity.setEntityId(dmp);
                    apiContext.getOperationsContext().getDatabaseRepository().getEntityDoiDao().createOrUpdate(doiEntity);

                    dmp.getDois().add(doiEntity);
                    apiContext.getOperationsContext().getDatabaseRepository().getDmpDao().createOrUpdate(dmp);
                }
            }
        }
        Files.deleteIfExists(file.getFile().toPath());

        return finalDoi;
    }

    public Doi createDoi(DepositRequest depositRequest, Principal principal) throws Exception {
        DMP dmp = this.apiContext.getOperationsContext().getDatabaseRepository().getDmpDao().find(UUID.fromString(depositRequest.getDmpId()));
        if (!isUserOwnerOfDmp(dmp, principal))
            throw new Exception("User is not authorized to invoke this action");
        if (!dmp.getStatus().equals(DMP.DMPStatus.FINALISED.getValue()))
            throw new Exception("DMP is not finalized");
        /*if (dmp.getDoi() != null)
            throw new Exception("DMP already has a DOI");*/

        FileEnvelope file = getWordDocument(depositRequest.getDmpId(), principal, configLoader);
        String name = file.getFilename().substring(0, file.getFilename().length() - 5).replace(" ", "_").replace(",", "_");
        File pdfFile = PDFUtils.convertToPDF(file, environment);
        String fileName = name + ".pdf";
        ResponseEntity<byte[]> jsonFile;
        try {
            jsonFile = getRDAJsonDocument(depositRequest.getDmpId(), principal);
        } catch (Exception e) {
            throw e;
        }
        String previousDOI = this.getPreviousDOI(dmp.getGroupId(), dmp.getId(), depositRequest.getRepositoryId());

        DMPDepositModel dmpDepositModel = DMPToDepositMapper.fromDMP(dmp, pdfFile, fileName, jsonFile, previousDOI);

//        String zenodoToken = "";
//        try {
//            if (this.userManager.isDOITokenValid(principal)) {
//                zenodoToken = principal.getZenodoToken();
//            }
//        } catch (NonValidTokenException e) {
//            zenodoToken = this.environment.getProperty("zenodo.access_token");
//        }

        Optional<RepositoryDeposit> repo = this.repositoriesDeposit.stream().filter(x -> x.getConfiguration().getRepositoryId().equals(depositRequest.getRepositoryId())).findFirst();
        String finalDoi = repo.map(r -> {
            try {
                return r.deposit(dmpDepositModel, false, depositRequest.getAccessToken());
            } catch (Exception e) {
                logger.error(e.getMessage(), e);
                return null;
            }
        }).orElse(null);
        Doi doiModel = null;
        if (finalDoi != null) {

            EntityDoi doiEntity = new EntityDoi();
            doiEntity.setId(UUID.randomUUID());
            doiEntity.setEntityType(EntityDoi.EntityType.DMP);
            doiEntity.setDoi(finalDoi);
            doiEntity.setRepositoryId(depositRequest.getRepositoryId());
            Date now = new Date();
            doiEntity.setCreatedAt(now);
            doiEntity.setUpdatedAt(now);
            doiEntity.setEntityId(dmp);
            apiContext.getOperationsContext().getDatabaseRepository().getEntityDoiDao().createOrUpdate(doiEntity);

            dmp.getDois().add(doiEntity);
            apiContext.getOperationsContext().getDatabaseRepository().getDmpDao().createOrUpdate(dmp);

            doiModel = new Doi().fromDataModel(doiEntity);

        }
        Files.deleteIfExists(file.getFile().toPath());

        return doiModel;
    }

    /*
    * Misc
    * */

    private void sendNotification(DMP dmp, UserInfo user, NotificationType notificationType) {
        List<UserDMP> userDMPS = databaseRepository.getUserDmpDao().asQueryable().where(((builder, root) -> builder.equal(root.get("dmp").get("id"), dmp.getId()))).toList();
        for (UserDMP userDMP : userDMPS) {
            if (!userDMP.getUser().getId().equals(user.getId())) {
                Notification notification = new Notification();
                notification.setUserId(user);
                notification.setType(notificationType);
                notification.setNotifyState(NotifyState.PENDING);
                notification.setIsActive(ActiveStatus.ACTIVE);
                notification.setData("{" +
                        "\"userId\": \"" + userDMP.getUser().getId() + "\"" +
                        ", \"id\": \"" + userDMP.getDmp().getId() + "\"" +
                        ", \"name\": \"" + userDMP.getDmp().getLabel() + "\"" +
                        ", \"path\": \"" + notificationPaths.get(notificationType) +"\"" +
                        "}");
                notification.setCreatedAt(new Date());
                notification.setUpdatedAt(notification.getCreatedAt());
                notification.setContactTypeHint(ContactType.EMAIL);
                notification.setContactHint(userDMP.getUser().getEmail());
                databaseRepository.getNotificationDao().createOrUpdate(notification);
            }
        }

    }

    /*public DataTableData<DataManagementPlanListingModel> getPublicPaged(DataManagmentPlanPublicTableRequest dataManagementPlanPublicTableRequest, String fieldsGroup, Principal principal) throws Exception {
        dataManagementPlanPublicTableRequest.setQuery(databaseRepository.getDmpDao().asQueryable().withHint(HintedModelFactory.getHint(DataManagementPlanListingModel.class)));
        QueryableList<DMP> items = dataManagementPlanPublicTableRequest.applyCriteria();

        if (principal.getId() != null && dataManagementPlanPublicTableRequest.getCriteria().getRole() != null) {
            items.where((builder, root) -> {
                Join userJoin = root.join("users", JoinType.LEFT);
                return builder.and(builder.equal(userJoin.join("user", JoinType.LEFT).get("id"), principal.getId()), builder.equal(userJoin.get("role"), dataManagementPlanPublicTableRequest.getCriteria().getRole()));
            });
        }
        QueryableList<DMP> pagedItems = PaginationManager.applyPaging(items, dataManagementPlanPublicTableRequest);

        DataTableData<DataManagementPlanListingModel> dataTable = new DataTableData<>();

        CompletableFuture itemsFuture;
        if (fieldsGroup.equals("listing")) {
            itemsFuture = pagedItems.withHint(HintedModelFactory.getHint(DataManagementPlanListingModel.class))
                    .selectAsync(item -> {
                        item.setDataset(
                                item.getDataset().stream()
                                        .filter(dataset -> dataset.getStatus().equals(Dataset.Status.FINALISED.getValue())).collect(Collectors.toSet()));
                        return new DataManagementPlanListingModel().fromDataModelDatasets(item);
                    })
                    .whenComplete((resultList, throwable) -> dataTable.setData(resultList));
        } else {
            itemsFuture = pagedItems
                    .selectAsync(item -> new DataManagementPlanListingModel().fromDataModel(item))
                    .whenComplete((resultList, throwable) -> dataTable.setData(resultList));
        }

        CompletableFuture countFuture = items.countAsync().whenComplete((count, throwable) -> {
            dataTable.setTotalCount(count);
        });
        CompletableFuture.allOf(itemsFuture, countFuture).join();
        return dataTable;
    }
*/
    /*public void unlock(UUID uuid) throws Exception {
        apiContext.getOperationsContext().getDatabaseRepository().getDmpDao()
                .asQueryable().where((builder, root) -> builder.equal(root.get("id"), uuid))
                .update(root -> root.get("status"), DMP.DMPStatus.ACTIVE.getValue());
        return;
    }*/
    /*public File getPdfDocument(String id) throws InstantiationException, IllegalAccessException, InterruptedException, IOException {
        File file = this.getWordDocument(id);
        String fileName = file.getName();
        if (fileName.endsWith(".docx")){
            fileName = fileName.substring(0, fileName.length() - 5);
        }
        return this.datasetManager.convertToPDF(file, environment, fileName);
    }*/
    /*public eu.eudat.models.data.dmp.DataManagementPlan getSinglePublic(String id, DynamicGrantConfiguration dynamicGrantConfiguration) throws Exception {
        DMP dataManagementPlanEntity = databaseRepository.getDmpDao().find(UUID.fromString(id));

        if (dataManagementPlanEntity != null && dataManagementPlanEntity.getStatus() == 1) {
            eu.eudat.models.data.dmp.DataManagementPlan datamanagementPlan = new eu.eudat.models.data.dmp.DataManagementPlan();
            datamanagementPlan.fromDataModel(dataManagementPlanEntity);
            datamanagementPlan.setDatasets(datamanagementPlan.getDatasets().stream().filter(dataset -> dataset.getStatus() == Dataset.Status.FINALISED.getValue()).collect(Collectors.toList()));
            Map dmpProperties = dataManagementPlanEntity.getDmpProperties() != null ? new org.json.JSONObject(dataManagementPlanEntity.getDmpProperties()).toMap() : null;
//            datamanagementPlan.setDynamicFields(dynamicGrantConfiguration.getFields().stream().map(item -> {
//                DynamicFieldWithValue fieldWithValue = new DynamicFieldWithValue();
//                fieldWithValue.setId(item.getId());
//                fieldWithValue.setDependencies(item.getDependencies());
//                fieldWithValue.setName(item.getName());
//                fieldWithValue.setQueryProperty(item.getQueryProperty());
//                fieldWithValue.setRequired(item.getRequired());
//                return fieldWithValue;
//            }).collect(Collectors.toList()));

            if (dmpProperties != null && datamanagementPlan.getDynamicFields() != null)
                datamanagementPlan.getDynamicFields().forEach(item -> {
                    Map<String, String> properties = (Map<String, String>) dmpProperties.get(item.getId());
                    if (properties != null)
                        item.setValue(new Tuple<>(properties.get("id"), properties.get("label")));
                });
            return datamanagementPlan;
        } else {
            throw new Exception("Selected DMP is not public");
        }
    }

    public DataManagementPlanOverviewModel getOverviewSinglePublic(String id) throws Exception {
        DMP dataManagementPlanEntity = databaseRepository.getDmpDao().find(UUID.fromString(id));
        if (dataManagementPlanEntity != null && dataManagementPlanEntity.getStatus() == 1) {
            DataManagementPlanOverviewModel datamanagementPlan = new DataManagementPlanOverviewModel();
            datamanagementPlan.fromDataModelDatasets(dataManagementPlanEntity);
            datamanagementPlan.setDatasets(datamanagementPlan.getDatasets().stream().filter(dataset -> dataset.getStatus() == Dataset.Status.FINALISED.getValue()).collect(Collectors.toList()));
            return datamanagementPlan;
        } else {
            throw new Exception("Selected DMP is not public");
        }
    }*/
    /*public List<DataManagementPlan> getWithCriteria(DMPDao dmpsRepository, DataManagementPlanCriteriaRequest dataManagementPlanCriteria, Principal principal) throws IllegalAccessException, InstantiationException {
        UUID principalID = principal.getId();
        QueryableList<DMP> items = dmpsRepository.getWithCriteria(dataManagementPlanCriteria.getCriteria()).withHint(HintedModelFactory.getHint(DataManagementPlan.class));
        List<Integer> roles = new LinkedList<>();
        QueryableList<DMP> authenticatedItems = dmpsRepository.getAuthenticated(items, principalID, roles);
        List<eu.eudat.models.data.dmp.DataManagementPlan> datamanagementPlans = authenticatedItems.select(item -> new DataManagementPlan().fromDataModel(item));
        return datamanagementPlans;
    }*/
}
