package eu.dnetlib.dli.proto;

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

import com.googlecode.protobuf.format.JsonFormat;
import eu.dnetlib.data.proto.*;
import eu.dnetlib.data.proto.DliFieldTypeProtos;
import eu.dnetlib.data.proto.DliProtos;
import eu.dnetlib.data.proto.dli.DLIObjectProtos;
import org.apache.commons.lang3.StringUtils;

import static eu.dnetlib.data.proto.dli.DLIObjectProtos.DLIObjectSummary;
import static eu.dnetlib.data.proto.dli.DLIObjectProtos.TypedIdentifier;

/**
 * Created by sandro on 3/13/17.
 */

public class DNGFDLISummaryConverter {

    private DNGFProtos.DNGF mainDNGFEntity;

    private int relatedDatasets = 0;

    private int relatedPublications = 0;

    private int relatedUnknown = 0;


    public void setMainEntity(final DNGFProtos.DNGF mainEntity) {
        this.mainDNGFEntity = mainEntity;
    }

    public void addRelation(final DNGFProtos.DNGF relation) {
        final TypeProtos.Type targetType = relation.getRel().getTargetType();

        switch (targetType) {
            case dataset:
                this.relatedDatasets++;
                break;
            case publication:
                this.relatedPublications++;
                break;
            case unknown:
                this.relatedUnknown++;
                break;
        }

    }

    public String convertAsJson() {
        final DLIObjectSummary convert = convert();
        if (convert != null)
            return JsonFormat.printToString(convert);
        return null;
    }

    public DLIObjectSummary convert() {
        if (!isValid()) {
            return null;
        }

        final DNGFProtos.DNGFEntity entity = mainDNGFEntity.getEntity();
        final DLIObjectSummary.Builder summary = DLIObjectSummary.newBuilder();

        summary.setId(entity.getId());
        final List<FieldTypeProtos.StructuredProperty> identifiers = entity.getExtension(DliProtos.typedIdentifier);
        if (identifiers == null || identifiers.isEmpty()) {
            this.mainDNGFEntity = null;
            return null;
//            throw new IllegalStateException(String.format("missing typedIdentifiers on main Entity \n%s", entity));
        }

        //ADD ALL IDENTIFIERS
        identifiers.stream()
                .collect(
                        Collectors.groupingBy(s -> String.format("%s::%s", s.getValue().toLowerCase().trim(), s.getQualifier().getClassid().toLowerCase().trim())))
                .values()
                .forEach(
                        v -> summary.addLocalIdentifier(
                                TypedIdentifier.newBuilder()
                                        .setId(v.get(0).getValue())
                                        .setType(v.get(0).getQualifier().getClassid())
                        ));


        // ADD ALL DATASOURCES
        final Map<String, DLIObjectProtos.CollectedFromType> datasources = new HashMap<>();


        final List<FieldTypeProtos.KeyValue> collectedFromList = entity.getCollectedfromList();
        if (collectedFromList != null) {
            collectedFromList.forEach(collectedFromValue -> {
                        try {
                            datasources.put(collectedFromValue.getKey(),
                                    DLIObjectProtos.CollectedFromType.newBuilder()
                                            .setDatasourceId(collectedFromValue.getKey())
                                            .setDatasourceName(collectedFromValue.getValue())
                                            .setCompletionStatus(DLIObjectProtos.CompletionStatus.valueOf(collectedFromValue.getExtension(DliFieldTypeProtos.completionStatus)))
                                            .build());
                        } catch (IllegalArgumentException e) {
                            System.out.println("Error on generate datasource from record " + entity.toString());
                            throw new RuntimeException(e);
                        }
                    }
            );
        }
        final List<FieldTypeProtos.KeyValue> resolvedFromList = entity.getExtension(DliProtos.resolvedfrom);
        if (resolvedFromList != null) {
            Map<String, FieldTypeProtos.KeyValue> setRFrom = new HashMap<>();
            resolvedFromList.forEach(resolvedFromValue -> setRFrom.put(resolvedFromValue.getKey(), resolvedFromValue));
            setRFrom.values().forEach(r ->
                    datasources.put(r.getKey(),
                            DLIObjectProtos.CollectedFromType.newBuilder()
                                    .setCompletionStatus(DLIObjectProtos.CompletionStatus.complete)
                                    .setDatasourceId(r.getKey())
                                    .setDatasourceName(r.getValue())
                                    .build())
            );
        }
        summary.addAllDatasources(datasources.values());

        final Set<String> authorList = DngfDliUtils.parseAuthor(entity);

        switch (entity.getType()) {
            case dataset:
                summary.setTypology(DLIObjectProtos.Typology.dataset);
                setMetadataFromDataset(entity.getDataset(), summary, authorList);
                break;
            case publication:
                summary.setTypology(DLIObjectProtos.Typology.publication);
                setMetadataFromPublication(entity.getPublication(), summary, authorList);
                break;
            case unknown:
                summary.setTypology(DLIObjectProtos.Typology.unknown);
                break;
            default:
                throw new IllegalStateException(String.format("wrong Type for mainEntity: \n%s", entity));
        }

        summary.addAllPublisher(DngfDliUtils.parsePublishers(mainDNGFEntity.getEntity()));
        summary.setRelatedPublications(relatedPublications);
        summary.setRelatedDatasets(relatedDatasets);
        summary.setRelatedUnknown(relatedUnknown);

        return summary.build();
    }



    private void setMetadataFromDataset(final DatasetProtos.Dataset dataset, final DLIObjectSummary.Builder metadata, final Set<String> authorSet) {
        final List<FieldTypeProtos.StructuredProperty> titleList = dataset.getMetadata().getTitleList();
        //ADD Titles
        if (titleList != null) {
            titleList.forEach(title -> metadata.addTitle(title.getValue()));
        }

        //ADD AUTHORS
        final List<PersonProtos.Person> authorList = dataset.getAuthorList();
        if (authorList != null) {
            authorList.forEach(person -> authorSet.add(person.getMetadata().getFullname().getValue()));
        }

        if (dataset.getMetadata().getContributorList() != null)
            dataset.getMetadata().getContributorList().forEach(c -> authorSet.add(c.getValue()));

        metadata.addAllAuthor(authorSet);

        //ADD Abstract
        manageDatasetDates(dataset, metadata);
        if (dataset.getMetadata().getDescriptionList() != null) {
            dataset.getMetadata().getDescriptionList().forEach(desc -> metadata.setAbstract(desc.getValue()));
        }

        //ADD Subjects
        if (dataset.getMetadata().getSubjectList() != null) {
            dataset.getMetadata().getSubjectList().forEach(
                    subject -> metadata.addSubject(
                            DLIObjectProtos.SchemeValue.newBuilder()
                                    .setValue(subject.getValue())
                                    .setScheme(subject.getQualifier().getClassid())
                                    .build()
                    )
            );
        }


    }

    private void setMetadataFromPublication(final PublicationProtos.Publication publication, final DLIObjectSummary.Builder metadata, final Set<String> authorSet) {
        final List<FieldTypeProtos.StructuredProperty> titleList = publication.getMetadata().getTitleList();
        //ADD Titles
        if (titleList != null) {
            titleList.forEach(title -> metadata.addTitle(title.getValue()));
        }
        //ADD AUTHORS
        final List<PersonProtos.Person> authorList = publication.getAuthorList();
        if (authorList != null) {
            authorList.forEach(person -> authorSet.add(person.getMetadata().getFullname().getValue()));
        }

        if (publication.getMetadata().getContributorList() != null)
            publication.getMetadata().getContributorList().forEach(c -> authorSet.add(c.getValue()));

        metadata.addAllAuthor(authorSet);

        //ADD Abstract
        managePublicationDates(publication, metadata);
        if (publication.getMetadata().getDescriptionList() != null) {
            publication.getMetadata().getDescriptionList().forEach(desc -> metadata.setAbstract(desc.getValue()));
        }

        //ADD Subjects
        if (publication.getMetadata().getSubjectList() != null) {
            publication.getMetadata().getSubjectList().forEach(
                    subject -> metadata.addSubject(
                            DLIObjectProtos.SchemeValue.newBuilder()
                                    .setValue(subject.getValue())
                                    .setScheme(subject.getQualifier().getClassid())
                                    .build()
                    )
            );
        }


    }

    private void managePublicationDates(final PublicationProtos.Publication publication, final DLIObjectSummary.Builder metadata) {
        if (publication.getMetadata().getDateofacceptance() != null && StringUtils.isNotEmpty(publication.getMetadata().getDateofacceptance().toString())) {
            metadata.addDate(publication.getMetadata().getDateofacceptance().getValue());
        }

//        if (publication.getMetadata().getRelevantdateList() != null && !publication.getMetadata().getRelevantdateList().isEmpty()) {
//            publication.getMetadata().getRelevantdateList().forEach(
//                    date -> metadata.addDate(date.getValue())
//            );
//        }
    }

    private void manageDatasetDates(final DatasetProtos.Dataset dataset, final DLIObjectSummary.Builder metadata) {
        if (dataset.getMetadata().getDateofacceptance() != null && StringUtils.isNotEmpty(dataset.getMetadata().getDateofacceptance().toString())) {
            metadata.addDate(dataset.getMetadata().getDateofacceptance().getValue());
        }

        if (dataset.getMetadata().getRelevantdateList() != null && !dataset.getMetadata().getRelevantdateList().isEmpty()) {
            dataset.getMetadata().getRelevantdateList().forEach(
                    date -> {
                        if (date.getValue()!= null && StringUtils.isNotEmpty(date.getValue()))
                            metadata.addDate(date.getValue());
                    }
            );
        }

    }


    public boolean isValid() {
        return mainDNGFEntity != null && !(this.relatedUnknown == 0 && this.relatedDatasets == 0 && this.relatedPublications == 0);
    }

    public DNGFProtos.DNGF getMainDNGFEntity() {
        return mainDNGFEntity;
    }

    public void setMainDNGFEntity(DNGFProtos.DNGF mainDNGFEntity) {
        this.mainDNGFEntity = mainDNGFEntity;
    }
}
