package eu.dnetlib.dli.resolver;

import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.mongodb.MongoClient;
import com.mongodb.client.FindIterable;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.model.Filters;
import eu.dnetlib.dli.resolver.model.CompletionStatus;
import eu.dnetlib.dli.resolver.model.DLIObjectProvenance;
import eu.dnetlib.dli.resolver.model.DLIResolvedObject;
import eu.dnetlib.dli.resolver.model.ObjectProvisionMode;
import eu.dnetlib.enabling.tools.DnetStreamSupport;
import eu.dnetlib.pid.resolver.AbstractPIDResolver;
import eu.dnetlib.pid.resolver.model.ObjectType;
import eu.dnetlib.pid.resolver.model.SubjectType;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.bson.Document;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.beans.factory.annotation.Value;


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


public class DataciteOfflineResolver extends AbstractPIDResolver {

    /**
     * The Constant log.
     */
    private static final Log log = LogFactory.getLog(DataciteOfflineResolver.class);

    private static String esUrl = "http://ip-90-147-167-25.ct1.garrservices.it:9200/datacite/dump/";

    @Override
    protected boolean canResolvePid(final String pidType) {
        return (pidType != null) && ("doi".equals(pidType.toLowerCase().trim()) || "handle".equals(pidType.toLowerCase().trim()));

    }

    @Override
    public DLIResolvedObject resolve(final String pid, final String pidType) {
        final String result = requestURL(esUrl + pid.replaceAll("/","%2F"));
        return parseResponse(result,pid.equals("10.15468/dl.cdvqih"));
    }

    private DLIResolvedObject parseResponse(String result, final boolean skipAbstract) {
        if (result == null) {
            return null;
        }
        final JsonParser p = new JsonParser();
        JsonObject root = p.parse(result).getAsJsonObject();
        if (!(root.has("found") && root.get("found").getAsBoolean()))
            return null;

        final DLIResolvedObject dli = new DLIResolvedObject();

        root = root.getAsJsonObject("_source").getAsJsonObject("attributes");

        if (skipAbstract==false && root.has("descriptions") && root.get("descriptions").isJsonArray()) {
            final JsonArray descriptions = root.get("descriptions").getAsJsonArray();
            for (JsonElement description: descriptions) {
                if (!description.isJsonNull() && description.isJsonObject()) {
                    dli.setDescription(getStringValue(description.getAsJsonObject(),"description"));
                }
            }
        }
        String current_date= null;
        if (root.has("dates") && root.get("dates").isJsonArray()) {
            final JsonArray dates = root.get("dates").getAsJsonArray();
            for (JsonElement date: dates) {
                if (!date.isJsonNull() && date.isJsonObject()) {
                    final String tmp = getStringValue(date.getAsJsonObject(),"date");
                    if (current_date == null && StringUtils.isNotBlank(tmp)) {
                        current_date = tmp;
                    } else if (current_date != null && StringUtils.isNotBlank(tmp) && tmp.length() > current_date.length()) {
                        current_date = tmp;
                    }
                }
            }
        }

        final List<String> currentTitles = new ArrayList<>();
        if (root.has("titles") && root.get("titles").isJsonArray()) {
            final JsonArray titles = root.get("titles").getAsJsonArray();
            for (JsonElement title: titles) {
                if (title!=null && !title.isJsonNull() && title.isJsonObject()) {
                    currentTitles.add(getStringValue(title.getAsJsonObject(),"title"));
                }

            }
        }

        final List<String> authors = new ArrayList<>();
        if (root.has("creators") && root.get("creators").isJsonArray()) {
            final JsonArray creators = root.get("creators").getAsJsonArray();
            for (JsonElement creator: creators) {
                if (creator!=null && !creator.isJsonNull() && creator.isJsonObject()) {
                    authors.add(getStringValue(creator.getAsJsonObject(),"name"));
                }

            }
        }
        dli.setAuthors(authors);
        dli.setTitles(currentTitles);
        dli.setDate(current_date);
        final List<SubjectType> current_subjects = new ArrayList<>();
        if (root.has("subjects") && root.get("subjects").isJsonArray()) {
            final JsonArray subjects = root.get("subjects").getAsJsonArray();
            for (JsonElement subject: subjects) {
                if (subject!=null && !subject.isJsonNull() && subject.isJsonObject()) {
                    final String scheme = getStringValue(subject.getAsJsonObject(),"subjectScheme");
                    final String term = getStringValue(subject.getAsJsonObject(),"subject");
                    current_subjects.add(new SubjectType(scheme, term));
                }
            }
        }
        dli.setSubjects(current_subjects);
        dli.setPid(getStringValue(root, "doi"));
        dli.setPidType("doi");
        dli.setType(ObjectType.dataset);
        final DLIObjectProvenance provenance =new DLIObjectProvenance();
        provenance.setDatasourceId("dli_________::datacite");
        provenance.setCompletionStatus(CompletionStatus.complete.toString());
        provenance.setProvisionMode(ObjectProvisionMode.resolved.toString());
        provenance.setDatasource("Datasets in Datacite");
        String publisher = getStringValue(root, "publisher");

        provenance.setPublisher(publisher);

        dli.setDatasourceProvenance(Collections.singletonList(provenance));
        dli.setCompletionStatus(CompletionStatus.complete.toString());
        return dli;
    }

    private SubjectType createSubjectType(final JsonObject item){
        if (item.has("term") && !item.get("term").isJsonNull())
            return new SubjectType(item.get("scheme").getAsString(), item.get("term").getAsString());
        else {
            return null;
        }
    }

    private List<String> getStringValues(final JsonObject root, final String key) {

        if (root.has(key) && root.get(key).isJsonArray()) {
            return DnetStreamSupport.generateStreamFromIterator(root.getAsJsonArray(key).iterator())
                    .map(JsonElement::getAsString)
                    .collect(Collectors.toList());
        }
        return null;
    }


    private String getStringValue(final JsonObject root, final String key) {
        if (root.has(key) && !root.get(key).isJsonNull()) {
            if (root.get(key).isJsonArray()){
                return root.get(key).getAsJsonArray().get(0).getAsString();
            }
            else {
                return root.get(key).getAsString();
            }
        }
        return null;
    }

    @Override
    public boolean isAvailableOffline() {
        return true;
    }
}
