package eu.dnetlib.openaire.community;

import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import com.google.gson.Gson;
import eu.dnetlib.openaire.common.ISClient;
import eu.dnetlib.openaire.exporter.exceptions.CommunityException;
import eu.dnetlib.openaire.exporter.exceptions.CommunityNotFoundException;
import eu.dnetlib.openaire.exporter.model.community.CommunityContentprovider;
import eu.dnetlib.openaire.exporter.model.community.CommunityDetails;
import eu.dnetlib.openaire.exporter.model.community.CommunityOrganization;
import eu.dnetlib.openaire.exporter.model.community.CommunityProject;
import eu.dnetlib.openaire.exporter.model.community.CommunitySummary;
import eu.dnetlib.openaire.exporter.model.community.CommunityWritableProperties;
import eu.dnetlib.openaire.exporter.model.community.CommunityZenodoCommunity;
import eu.dnetlib.openaire.exporter.model.context.Category;
import eu.dnetlib.openaire.exporter.model.context.Concept;
import eu.dnetlib.openaire.exporter.model.context.Context;
import eu.dnetlib.openaire.exporter.model.context.Param;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import static eu.dnetlib.openaire.community.CommunityConstants.*;

@Component
public class CommunityCommon {

    @Autowired
    private ISClient isClient;

    public Map<String, Context> getContextMap() throws CommunityException {
        try {
            return isClient.getCommunityContextMap();
        } catch (IOException e) {
            throw new CommunityException(e);
        }
    }

    public List<CommunitySummary> listCommunities()  throws CommunityException {
        return getContextMap().values().stream()
                .filter(context -> !communityBlackList.contains(context.getId()))
                .map(CommunityMappingUtils::asCommunitySummary)
                .collect(Collectors.toList());
    }


    public <R> List<R> getCommunityInfo(final String id, final String idSuffix, final Function<Concept, R> mapping) throws CommunityException {
        final Map<String, Context> contextMap = getContextMap();
        final Context context = contextMap.get(id);
        if (context != null) {
            final Map<String, Category> categories = context.getCategories();
            final Category category = categories.get(id + idSuffix);
            if (category != null) {
                return category.getConcepts().stream()
                        .map(mapping)
                        .collect(Collectors.toList());
            }
        }
        return Lists.newArrayList();
    }

    public CommunityDetails getCommunity(final String id) throws CommunityException, CommunityNotFoundException {
        final Context context = getContextMap().get(id);
        if (context == null || CommunityConstants.communityBlackList.contains(id)) {
            throw new CommunityNotFoundException(String.format("community '%s' does not exist", id));
        }
        return CommunityMappingUtils.asCommunityProfile(context);
    }

    public List<CommunityZenodoCommunity> getCommunityZenodoCommunities(final String id) throws CommunityException, CommunityNotFoundException {
        getCommunity(id); // ensure the community exists.
        return getCommunityInfo(id, ZENODOCOMMUNITY_ID_SUFFIX, c -> CommunityMappingUtils.asCommunityZenodoCommunity(id, c));
    }


    public void updateProject(String communityId, CommunityProject project) throws CommunityException {
        final Context context = getContextMap().get(communityId);
        Category prj = context.getCategories().get(communityId + PROJECTS_ID_SUFFIX);
        if (prj.getConcepts().stream().map(c -> c.getId()).collect(Collectors.toList())
                .contains(communityId + PROJECTS_ID_SUFFIX + ID_SEPARATOR + project.getId())){
            prj.getConcepts().forEach(concept -> {
                if (concept.getId().equals(communityId + PROJECTS_ID_SUFFIX + ID_SEPARATOR + project.getId())) {
                    if (project.getName() != null) {

                        concept.getParams().replace(CPROJECT_FULLNAME, Arrays.asList(new Param()
                                .setName(CPROJECT_FULLNAME).setValue(project.getName())));
                    }
                    if (project.getAcronym() != null) {
                        if(concept.getParams().keySet().contains(CPROJECT_ACRONYM)){
                            concept.getParams().replace(CPROJECT_ACRONYM, Arrays.asList(new Param()
                                    .setName(CPROJECT_ACRONYM).setValue(project.getAcronym())));
                        }
                        else{
                            concept.getParams().put(CPROJECT_ACRONYM, Arrays.asList(new Param()
                                    .setName(CPROJECT_ACRONYM).setValue(project.getAcronym())));
                        }

                    }
                    if (project.getOpenaireId() != null) {
                        if(concept.getParams().keySet().contains(OPENAIRE_ID)){
                            concept.getParams().replace(OPENAIRE_ID, Arrays.asList(new Param()
                                    .setName(OPENAIRE_ID).setValue(project.getOpenaireId())));
                        }
                        else{
                            concept.getParams().put(OPENAIRE_ID, Arrays.asList(new Param()
                            .setName(OPENAIRE_ID).setValue(project.getOpenaireId())));
                        }

                    }
                    if (project.getFunder() != null) {
                        concept.getParams().replace(CPROJECT_FUNDER, Arrays.asList(new Param()
                                .setName(CPROJECT_FUNDER).setValue(project.getFunder())));
                    }
                    if (project.getGrantId() != null) {
                        concept.getParams().replace(CPROJECT_NUMBER, Arrays.asList(new Param()
                                .setName(CPROJECT_NUMBER).setValue(project.getGrantId())));

                    }
                }
            });
        }
        else{
            Concept concept = new Concept();
            concept.setId(communityId + PROJECTS_ID_SUFFIX + ID_SEPARATOR + project.getId());
            concept.setClaim(false);
            if(project.getAcronym() != null)
                concept.setLabel(project.getAcronym());
            else
                concept.setLabel("");

             Map<String, List<Param>> params = new TreeMap<>();

             if(project.getAcronym() != null){
                 params.put(CPROJECT_ACRONYM, Arrays.asList(new Param().setName(CPROJECT_ACRONYM)
                         .setValue(project.getAcronym())));
             }

             if (project.getName() != null){
                 params.put(CPROJECT_FULLNAME, Arrays.asList(new Param()
                         .setName(CPROJECT_FULLNAME)
                         .setValue(project.getName())
                 ));
             }

             if (project.getOpenaireId() != null){
                 params.put(OPENAIRE_ID, Arrays.asList(new Param()
                         .setName(OPENAIRE_ID)
                         .setValue(project.getOpenaireId())
                 ));
             }

             if(project.getFunder() != null){
                 params.put(CPROJECT_FUNDER, Arrays.asList(new Param()
                         .setName(CPROJECT_FUNDER)
                         .setValue(project.getFunder())
                 ));
             }

             if (project.getGrantId()!=null){
                 params.put(CPROJECT_NUMBER, Arrays.asList(new Param()
                         .setName(CPROJECT_NUMBER)
                         .setValue(project.getGrantId())
                 ));
             }

            concept.setParams(params);
            prj.getConcepts().add(concept);


        }

    }

    public void removeAdvancedConstraint(String id) throws CommunityException {
        final Context context = getContextMap().get(id);
        context.getParams()
                .replace(CPROFILE_ADVANCED_CONSTRAINT, Arrays.asList(new Param()
                        .setName(CPROFILE_ADVANCED_CONSTRAINT).setValue(null)));
    }

    public void updateCommunity(String id, CommunityWritableProperties community) throws CommunityException {
        final Context context = getContextMap().get(id);

        if(community.getShortName() != null) {
            context.setLabel(community.getShortName());
        }

        if (community.getName() != null){
            context.getParams().replace(CSUMMARY_NAME, Arrays.asList(new Param()
            .setValue(community.getName()).setName(CSUMMARY_NAME)));
        }
        if(community.getDescription() != null) {
            context.getParams()
                    .replace(CSUMMARY_DESCRIPTION, Arrays.asList(new Param()
                            .setName(CSUMMARY_DESCRIPTION).setValue(community.getDescription())));
        }
        if(community.getLogoUrl() != null){
            context.getParams()
                    .replace(CSUMMARY_LOGOURL, Arrays.asList(new Param()
                    .setName(CSUMMARY_LOGOURL).setValue(community.getLogoUrl())));

        }
        if (community.getStatus() != null) {
            context.getParams()
                    .replace(CSUMMARY_STATUS, Arrays.asList(new Param()
                    .setName(CSUMMARY_STATUS).setValue(community.getStatus().name())));
        }
        if (community.getSubjects() != null) {
            context.getParams()
                    .replace(CPROFILE_SUBJECT, Arrays.asList(new Param().setName(CPROFILE_SUBJECT)
                            .setValue(Joiner.on(CSV_DELIMITER)
                                    .join(community.getSubjects()))));
        }
        if(community.getFos() != null){
            if (context.getParams().containsKey(CPROFILE_FOS))
            context.getParams()
                    .replace(CPROFILE_FOS, Arrays.asList(new Param().setName(CPROFILE_FOS)
                            .setValue(Joiner.on(CSV_DELIMITER)
                                    .join(community.getFos()))));
            else
                context.getParams().put(CPROFILE_FOS, Arrays.asList(new Param().setName(CPROFILE_FOS)
                        .setValue(Joiner.on(CSV_DELIMITER)
                                .join(community.getFos()))));
        }
        if(community.getSdg() != null){
            if(context.getParams().containsKey(CPROFILE_SDG))
            context.getParams()
                    .replace(CPROFILE_SDG, Arrays.asList(new Param().setName(CPROFILE_SDG)
                            .setValue(Joiner.on(CSV_DELIMITER)
                                    .join(community.getSdg()))));
            else
                context.getParams().put(CPROFILE_SDG, Arrays.asList(new Param().setName(CPROFILE_SDG)
                        .setValue(Joiner.on(CSV_DELIMITER)
                                .join(community.getSdg()))));
        }
        if (community.getAdvancedConstraints() != null) {
            if(context.getParams().containsKey(CPROFILE_ADVANCED_CONSTRAINT))
            context.getParams()
                    .replace(CPROFILE_ADVANCED_CONSTRAINT, Arrays.asList(new Param()
                            .setName(CPROFILE_ADVANCED_CONSTRAINT).setValue(new Gson().toJson(community.getAdvancedConstraints()))));
            else
                context.getParams().put(CPROFILE_ADVANCED_CONSTRAINT, Arrays.asList(new Param()
                        .setName(CPROFILE_ADVANCED_CONSTRAINT).setValue(new Gson().toJson(community.getAdvancedConstraints()))));
        }
        if(community.getMainZenodoCommunity() != null){
            context.getParams()
                    .replace(CSUMMARY_ZENODOC, Arrays.asList(new Param()
                            .setName(CSUMMARY_ZENODOC).setValue(community.getMainZenodoCommunity())));

        }

    }


    public void removeFromCategory(String communityId, String category, String conceptId) throws CommunityException {
        Map<String, Context> cmap = getContextMap();
        Context context = cmap.get(communityId);
        Map<String, Category> cat = context.getCategories();

        List<Concept> concepts = cat.get(communityId + category).getConcepts()
                .stream().filter(c -> !c.getId().equals(communityId + category + ID_SEPARATOR + conceptId)).collect(Collectors.toList());

         cat.get(communityId + category).setConcepts(concepts);
    }

    public void updateDatasource(String communityId, CommunityContentprovider cp) throws CommunityException {
        final Context context = getContextMap().get(communityId);
        Category dts = context.getCategories().get(communityId + CONTENTPROVIDERS_ID_SUFFIX);
        if (dts.getConcepts().stream().map(c -> c.getId()).collect(Collectors.toList())
                .contains(communityId + CONTENTPROVIDERS_ID_SUFFIX + ID_SEPARATOR + cp.getId())){
            dts.getConcepts().forEach(concept -> {
                if (concept.getId().equals(communityId + CONTENTPROVIDERS_ID_SUFFIX + ID_SEPARATOR + cp.getId())) {


                    if (cp.getName() != null) {
                        if(concept.getParams().keySet().contains(CCONTENTPROVIDER_NAME)){
                            concept.getParams().replace(CCONTENTPROVIDER_NAME, Arrays.asList(new Param()
                                    .setName(CCONTENTPROVIDER_NAME).setValue(cp.getName())));
                        }
                        else{
                            concept.getParams().put(CCONTENTPROVIDER_NAME, Arrays.asList(new Param()
                                    .setName(CCONTENTPROVIDER_NAME).setValue(cp.getName())));
                        }

                    }
                    if (cp.getOfficialname() != null) {
                        if(concept.getParams().keySet().contains(CCONTENTPROVIDER_OFFICIALNAME)){
                            concept.getParams().replace(CCONTENTPROVIDER_OFFICIALNAME, Arrays.asList(new Param()
                                    .setName(CCONTENTPROVIDER_OFFICIALNAME).setValue(cp.getOfficialname())));
                        }
                        else{
                            concept.getParams().put(CCONTENTPROVIDER_OFFICIALNAME, Arrays.asList(new Param()
                                    .setName(CCONTENTPROVIDER_OFFICIALNAME).setValue(cp.getOfficialname())));
                        }

                    }


                    if (cp.getOpenaireId() != null) {
                        if(concept.getParams().keySet().contains(OPENAIRE_ID)){
                            concept.getParams().replace(OPENAIRE_ID, Arrays.asList(new Param()
                                    .setName(OPENAIRE_ID).setValue(cp.getOpenaireId())));
                        }
                        else{
                            concept.getParams().put(OPENAIRE_ID, Arrays.asList(new Param()
                                    .setName(OPENAIRE_ID).setValue(cp.getOpenaireId())));
                        }

                    }

                    if (cp.getSelectioncriteria()  != null) {
                        if(concept.getParams().keySet().contains(CCONTENTPROVIDER_SELCRITERIA)){
                            concept.getParams().replace(CCONTENTPROVIDER_SELCRITERIA, Arrays.asList(new Param()
                                    .setName(CCONTENTPROVIDER_SELCRITERIA).setValue(cp.toJson())));
                        }
                        else{
                            concept.getParams().put(CCONTENTPROVIDER_SELCRITERIA, Arrays.asList(new Param()
                                    .setName(CCONTENTPROVIDER_SELCRITERIA).setValue(cp.toJson())));
                        }

                    }
                }
            });
        }
        else{
            Concept concept = new Concept();
            concept.setId(communityId + CONTENTPROVIDERS_ID_SUFFIX + ID_SEPARATOR + cp.getId());
            concept.setClaim(false);
            concept.setLabel("");

            Map<String, List<Param>> params = new TreeMap<>();

            if (cp.getName() != null) {
                params.put( CCONTENTPROVIDER_NAME, Arrays.asList(new Param().setValue(cp.getName()).setName(CCONTENTPROVIDER_NAME)));
            }
            if(cp.getOfficialname()!= null){
                params.put( CCONTENTPROVIDER_OFFICIALNAME, Arrays.asList(new Param().setValue(cp.getOfficialname()).setName(CCONTENTPROVIDER_OFFICIALNAME)));
            }
            if (cp.getOpenaireId() != null){
                params.put( OPENAIRE_ID, Arrays.asList(new Param().setValue(cp.getOpenaireId()).setName(OPENAIRE_ID)));
            }
            if(cp.getSelectioncriteria() != null){
                params.put( CCONTENTPROVIDER_SELCRITERIA, Arrays.asList(new Param().setValue(cp.toJson()).setName(CCONTENTPROVIDER_SELCRITERIA)));

            }

            concept.setParams(params);
            dts.getConcepts().add(concept);


        }

    }

    public void updateOrganization(String communityId, CommunityOrganization organization) throws CommunityException {

        final Context context = getContextMap().get(communityId);
        Category orgs = context.getCategories().get(communityId + ORGANIZATION_ID_SUFFIX);
        if (orgs.getConcepts().stream().map(c -> c.getId()).collect(Collectors.toList())
                .contains(communityId + ORGANIZATION_ID_SUFFIX + ID_SEPARATOR + organization.getId())){
            orgs.getConcepts().forEach(concept -> {
                if (concept.getId().equals(communityId + ORGANIZATION_ID_SUFFIX + ID_SEPARATOR + organization.getId())) {


                    if (organization.getName() != null) {
                        if(concept.getParams().keySet().contains(CORGANIZATION_NAME)){
                            concept.getParams().replace(CORGANIZATION_NAME, Arrays.asList(new Param()
                                    .setName(CORGANIZATION_NAME).setValue(organization.getName())));
                        }
                        else{
                            concept.getParams().put(CORGANIZATION_NAME, Arrays.asList(new Param()
                                    .setName(CORGANIZATION_NAME).setValue(organization.getName())));
                        }

                    }
                    if (organization.getLogo_url() != null) {
                        if(concept.getParams().keySet().contains(CORGANIZATION_LOGOURL)){
                            concept.getParams().replace(CORGANIZATION_LOGOURL, Arrays.asList(new Param()
                                    .setName(CORGANIZATION_LOGOURL).setValue(Base64.getEncoder().encodeToString(organization.getLogo_url().getBytes()))));
                        }
                        else{
                            concept.getParams().put(CORGANIZATION_LOGOURL, Arrays.asList(new Param()
                                    .setName(CORGANIZATION_LOGOURL).setValue(Base64.getEncoder().encodeToString(organization.getLogo_url().getBytes()))));
                        }

                    }


                    if (organization.getWebsite_url() != null) {
                        if(concept.getParams().keySet().contains(CORGANIZATION_WEBSITEURL)){
                            concept.getParams().replace(CORGANIZATION_WEBSITEURL, Arrays.asList(new Param()
                                    .setName(CORGANIZATION_WEBSITEURL).setValue(Base64.getEncoder().encodeToString(organization.getWebsite_url().getBytes()))));
                        }
                        else{
                            concept.getParams().put(CORGANIZATION_WEBSITEURL, Arrays.asList(new Param()
                                    .setName(CORGANIZATION_WEBSITEURL).setValue(Base64.getEncoder().encodeToString(organization.getWebsite_url().getBytes()))));
                        }

                    }

                    }

            });
        }
        else{

            Concept concept = new Concept();
            concept.setId(communityId + ORGANIZATION_ID_SUFFIX + ID_SEPARATOR + organization.getId());
            concept.setClaim(false);
            concept.setLabel("");

            Map<String, List<Param>> params = new TreeMap<>();

            if (organization.getName() != null) {
                params.put( CORGANIZATION_NAME, Arrays.asList(new Param().setValue(organization.getName()).setName(CORGANIZATION_NAME)));
            }
            if(organization.getLogo_url()!= null){

                params.put( CORGANIZATION_LOGOURL, Arrays.asList(new Param().setValue(Base64.getEncoder().encodeToString(organization.getLogo_url().getBytes())).setName(CORGANIZATION_LOGOURL)));
            }
            if (organization.getWebsite_url() != null){
                params.put( CORGANIZATION_WEBSITEURL, Arrays.asList(new Param().setValue(Base64.getEncoder().encodeToString(organization.getWebsite_url().getBytes())).setName(CORGANIZATION_WEBSITEURL)));
            }


            concept.setParams(params);
            orgs.getConcepts().add(concept);


        }

    }

    public void updateZenodoCommunity(String communityId, CommunityZenodoCommunity zc) throws CommunityException {
        final Context context = getContextMap().get(communityId);
        Category zcs = context.getCategories().get(communityId + ZENODOCOMMUNITY_ID_SUFFIX);
        if (zcs.getConcepts().stream().map(c -> c.getId()).collect(Collectors.toList())
                .contains(communityId + ZENODOCOMMUNITY_ID_SUFFIX + ID_SEPARATOR + zc.getId())){
            zcs.getConcepts().forEach(concept -> {
                if (concept.getId().equals(communityId + ZENODOCOMMUNITY_ID_SUFFIX + ID_SEPARATOR + zc.getId())) {


                    if (zc.getZenodoid() != null) {
                        if(concept.getParams().keySet().contains(CZENODOCOMMUNITY_ID)){
                            concept.getParams().replace(CZENODOCOMMUNITY_ID, Arrays.asList(new Param()
                                    .setName(CZENODOCOMMUNITY_ID).setValue(zc.getZenodoid())));
                        }
                        else{
                            concept.getParams().put(CZENODOCOMMUNITY_ID, Arrays.asList(new Param()
                                    .setName(CZENODOCOMMUNITY_ID).setValue(zc.getZenodoid())));
                        }

                    }

                }

            });
        }
        else{

            Concept concept = new Concept();
            concept.setId(communityId + ZENODOCOMMUNITY_ID_SUFFIX + ID_SEPARATOR + zc.getId());
            concept.setClaim(false);


            Map<String, List<Param>> params = new TreeMap<>();

            if (zc.getZenodoid() != null) {
                params.put( CZENODOCOMMUNITY_ID, Arrays.asList(new Param().setValue(zc.getZenodoid()).setName(CZENODOCOMMUNITY_ID)));
                concept.setLabel(zc.getZenodoid());
            }else{
                concept.setLabel("");
            }
            concept.setParams(params);
            zcs.getConcepts().add(concept);

        }

    }

}
