package eu.eudat.depositinterface.zenodorepository.mapper;

import eu.eudat.depositinterface.models.DMPDepositModel;
import eu.eudat.depositinterface.models.OrganisationDepositModel;
import eu.eudat.depositinterface.models.UserDMPDepositModel;
import eu.eudat.depositinterface.zenodorepository.config.DOIFunder;
import eu.eudat.depositinterface.zenodorepository.models.*;

import java.time.Instant;
import java.util.*;
import java.util.stream.Collectors;

public class DMPToZenodoMapper {

    public static ZenodoDeposit fromDMP(DMPDepositModel dmp, String zenodoCommunity, String zenodoAffiliation, String domain, List<DOIFunder> doiFunders) {
        Map<String, Object> extraProperties = dmp.getExtraProperties() != null ? new org.json.JSONObject(dmp.getExtraProperties()).toMap() : new HashMap<>();
        ZenodoDeposit deposit = new ZenodoDeposit();
        deposit.setMetadata(new ZenodoDepositMetadata());
        deposit.getMetadata().setTitle(dmp.getLabel());
        deposit.getMetadata().setUploadType("publication");
        deposit.getMetadata().setPublicationType("datamanagementplan");
        deposit.getMetadata().setDescription((dmp.getDescription() != null && !dmp.getDescription().isEmpty() ? dmp.getDescription() : "<p></p>"));
        deposit.getMetadata().setVersion(String.valueOf(dmp.getVersion()));
        if(zenodoCommunity != null && !zenodoAffiliation.isEmpty()) {
            ZenodoComunity community = new ZenodoComunity();
            community.setIdentifier(zenodoCommunity);
            deposit.getMetadata().setCommunities(Collections.singletonList(community));
        }
        if (extraProperties.get("visible") == null) {
            deposit.getMetadata().setAccessRight(ZenodoAccessRight.RESTRICTED);
            deposit.getMetadata().setAccessConditions("");
        } else {
            if (((Boolean) extraProperties.get("visible"))) {
                Instant publicationDate = Instant.parse(extraProperties.get("publicDate").toString());
                if (publicationDate.isBefore(Instant.now())) {
                    deposit.getMetadata().setAccessRight(ZenodoAccessRight.OPEN);
                } else {
                    deposit.getMetadata().setAccessRight(ZenodoAccessRight.EMBARGOED);
                    deposit.getMetadata().setEmbargoDate(publicationDate.toString());
                }

                if (extraProperties.get("license") != null) {
                    deposit.getMetadata().setLicense(((Map<?, ?>) extraProperties.get("license")).get("pid").toString());
                }
            } else {
                deposit.getMetadata().setAccessRight(ZenodoAccessRight.RESTRICTED);
                deposit.getMetadata().setAccessConditions("");
            }
        }
        if (dmp.isPublic()) {
            ZenodoRelator relator = new ZenodoRelator();
            relator.setIdentifier(domain + "/external/zenodo/" + dmp.getId().toString());
            relator.setRelation("isIdenticalTo");
            deposit.getMetadata().setRelatedIdentifiers(Collections.singletonList(relator));
        }
        deposit.getMetadata().setContributors(new LinkedList<>());
        List<ZenodoContributor> contributors = dmp.getUsers().stream().map(userDMP -> {
            ZenodoContributor contributor = new ZenodoContributor();
            contributor.setName(userDMP.getUser().getName());
            contributor.setType("ProjectMember");
            if (dmp.getOrganisations() != null && !dmp.getOrganisations().isEmpty()) {
                contributor.setAffiliation(dmp.getOrganisations()
                        .stream().map(OrganisationDepositModel::getLabel).collect(Collectors.joining(", ")));
            } else {
                if(zenodoAffiliation != null && !zenodoAffiliation.isEmpty()) {
                    contributor.setAffiliation(zenodoAffiliation);
                }
            }
            return contributor;
        }).collect(Collectors.toList());

        List<ZenodoContributor> researchers = dmp.getResearchers().stream().map(researcher -> {
            ZenodoContributor contributor = new ZenodoContributor();
            contributor.setName(researcher.getLabel());
            contributor.setType("Researcher");
            String referenceHead = researcher.getReference().split(":")[0];
            String referenceTail = researcher.getReference().replace(referenceHead + ":", "");
            contributor.setAffiliation(referenceHead);
            if (referenceHead.equalsIgnoreCase("ORCID")) {
                contributor.setOrcid(referenceTail);
            }
            return contributor;
        }).collect(Collectors.toList());

        deposit.getMetadata().getContributors().addAll(contributors);
        deposit.getMetadata().getContributors().addAll(researchers);

        if (dmp.getGrant().getReference() == null) {
            dmp.getGrant().setReference("dmp:" + dmp.getGrant().getId());
        }
        String grantReferenceHead = dmp.getGrant().getReference().split(":")[0];
        if (grantReferenceHead.equals("openaire")) {
            String grantReferenceTail = dmp.getGrant().getReference().split(":")[3];
            DOIFunder doiFunder = doiFunders.stream()
                    .filter(doiFunder1 -> dmp.getGrant().getFunder().getLabel().contains(doiFunder1.getFunder()) || doiFunder1.getFunder().contains(dmp.getGrant().getFunder().getLabel()))
                    .findFirst().orElse(null);
            if (doiFunder != null) {
                String finalId = doiFunder.getDOI() + "::" + grantReferenceTail;
                ZenodoGrant grant = new ZenodoGrant();
                grant.setId(finalId);
                deposit.getMetadata().setGrants(Collections.singletonList(grant));
            }
        }
        ZenodoContributor creator = new ZenodoContributor();
        creator.setName(dmp.getUsers().stream().filter(userDMP -> userDMP.getRole().equals(UserDMPDepositModel.UserDMPRoles.OWNER.getValue())).findFirst().get().getUser().getName());
        if (dmp.getOrganisations() != null && !dmp.getOrganisations().isEmpty()) {
            creator.setAffiliation(dmp.getOrganisations()
                    .stream().map(OrganisationDepositModel::getLabel).collect(Collectors.joining(", ")));
        } else {
            if(zenodoAffiliation != null && !zenodoAffiliation.isEmpty()) {
                creator.setAffiliation(zenodoAffiliation);
            }
        }
        deposit.getMetadata().setCreators(Collections.singletonList(creator));

        return deposit;
    }
}

