package eu.dnetlib.dli;

import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.PostConstruct;

import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.google.gson.Gson;
import eu.dnetlib.data.transform.Ontologies;
import eu.dnetlib.data.transform.OntologyLoader;
import eu.dnetlib.enabling.locators.UniqueServiceLocator;
import eu.dnetlib.miscutils.functional.xml.DnetXsltFunctions;
import eu.dnetlib.pid.resolver.model.PID;
import eu.dnetlib.rmi.enabling.ISLookUpException;
import eu.dnetlib.rmi.enabling.ISLookUpService;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class DLIUtils {

    public final static Map<String, Pair<String, String>> datasources = new HashMap<>();
    public static final Map<String, String> resolvedTypes = new HashMap<String, String>() {
        {
            put("pdb", "http://www.rcsb.org/pdb/explore/explore.do?structureId=%s");
            put("ncbi-n", "http://www.ncbi.nlm.nih.gov/gquery/?term=%s");
            put("pmid", "http://www.ncbi.nlm.nih.gov/pubmed/%s");
            put("pmcid", "http://www.ncbi.nlm.nih.gov/pmc/articles/%s");
            put("pubmedid", "http://www.ncbi.nlm.nih.gov/pubmed/%s");
            put("doi", "http://dx.doi.org/%s");
            put("genbank", "http://www.ncbi.nlm.nih.gov/nucest/%s?report=genbank");
            put("nuccore", "http://www.ncbi.nlm.nih.gov/nucest/%s?report=genbank");
            put("swiss-prot", "http://www.ncbi.nlm.nih.gov/nucest/%s?report=genbank");
            put("arrayexpress", "http://www.ncbi.nlm.nih.gov/nucest/%s?report=genbank");
            put("biomodels", "http://www.ncbi.nlm.nih.gov/nucest/%s?report=genbank");
            put("bmrb", "http://www.ncbi.nlm.nih.gov/nucest/%s?report=genbank");
            put("ena", "http://www.ncbi.nlm.nih.gov/nucest/%s?report=genbank");
            put("genbank", "http://www.ncbi.nlm.nih.gov/nucest/%s?report=genbank");
            put("geo", "http://www.ncbi.nlm.nih.gov/nucest/%s?report=genbank");
            put("ensembl", "http://www.ncbi.nlm.nih.gov/nucest/%s?report=genbank");
            put("mgi", "http://www.ncbi.nlm.nih.gov/nucest/%s?report=genbank");
            put("bind", "http://www.ncbi.nlm.nih.gov/nucest/%s?report=genbank");
            put("pride", "http://www.ncbi.nlm.nih.gov/nucest/%s?report=genbank");
            put("ddbj", "http://www.ncbi.nlm.nih.gov/nucest/%s?report=genbank");
            put("bioproject", "http://www.ncbi.nlm.nih.gov/nucest/%s?report=genbank");
            put("embl", "http://www.ncbi.nlm.nih.gov/nucest/%s?report=genbank");
            put("sra", "http://www.ncbi.nlm.nih.gov/nucest/%s?report=genbank");
        }

    };
    public static Ontologies ontologies;
    private static DLIUtils instance;
    private static BiMap<String, String> relations;

    private Map<String, String> dataciteDatasource;

    @Autowired
    private UniqueServiceLocator serviceLocator;

    private static BiMap<String, String> getRelationMap() {

        if (relations == null) {
            relations = HashBiMap.create();
            relations.put("IsCitedBy", "Cites");
            relations.put("IsSupplementTo", "IsSupplementedBy");
            relations.put("IsReferencedBy", "References");
        }
        return relations;
    }

    public static String getNameFromDataSourcePrefix(final String datasourcePrefix) throws ISLookUpException {
        if (datasources.keySet().size() == 0) {
            generateDSMap();
        }
        if (!datasources.containsKey(datasourcePrefix))
            return "";
        return datasources.get(datasourcePrefix).getRight();
    }

    public static String getIdFromDataSourcePrefix(final String datasourcePrefix) throws ISLookUpException {
        if (datasources.keySet().size() == 0) {
            generateDSMap();
        }
        if (!datasources.containsKey(datasourcePrefix))
            return "";
        return datasources.get(datasourcePrefix).getLeft();
    }

    public static String getPublisherName(final String publisher) {
        if (instance.getDataciteDatasource() != null) {
            return instance.getDataciteDatasource().get(publisher.trim().toLowerCase());
        }
        return "";
    }

    public static void generateDSMap() throws ISLookUpException {
        if (datasources.keySet().size() > 0)
            return;

        final String query = "for $x in collection('/db/DRIVER/RepositoryServiceResources/RepositoryServiceResourceType') "
                + "return concat($x//FIELD[./key='NamespacePrefix']/value/text(),'@--@',$x//FIELD[./key='DataSourceId']/value/text(),'@--@',$x//ENGLISH_NAME )";
        final ISLookUpService lookupService = instance.getServiceLocator().getService(ISLookUpService.class);
        final List<String> results = lookupService.quickSearchProfile(query);
        datasources.clear();
        if (results != null)
            results.forEach(it -> {
                final String[] splitted = it.split("@--@");
                if (splitted.length == 3) {
                    datasources.put(splitted[0], new ImmutablePair<>(splitted[1], splitted[2]));
                }
            });
    }

    public static String inferPidType(final String pid, final String pidType) {
        if (pidType != null && !pidType.toLowerCase().equals("doi")) {
            if (pid != null && pid.contains("http://dx.doi.org/") || pid.contains("http://doi.org/"))
                return "doi";
        }
        return pidType;
    }

    public static String fixPID(String input) {
        if (input != null) {
            return input.replace("http://dx.doi.org/", "").replace("http://doi.org/", "");
        }
        return null;
    }


    public static String geussPidType(final String pidType, final String pid ) {
        if (isValidDoi(pid)!= null){
            return "doi";
        }
        return pidType;
    }


    public static String geussPidValue(final String pid) {
        if (isValidDoi(pid)!= null){
            return isValidDoi(pid);
        }
        return pid;
    }



    public static PID createCorrectPID(final String pid, final String pidType) {
        final String validDoi = isValidDoi(pid);
        if (validDoi!= null) {
            return new PID(validDoi.toLowerCase(), "doi");
        }
        return new PID(pid, pidType);
    }

    public static String isValidDoi(final String url) {


        final String regex = "(10[.][0-9]{4,}(?:[.][0-9]+)*/(?:(?![\\\"&\\'])\\S)+)";


        final Pattern pattern = Pattern.compile(regex);
        final Matcher matcher = pattern.matcher(url);

        if (matcher.find())
            return matcher.group(0);


        return null;

    }

    public static String normalizeRelation(final String relation) {
        if (relation == null || StringUtils.isEmpty(relation)) {
            return null;
        }
        return Character.toLowerCase(relation.charAt(0)) + relation.substring(1);

    }

    public static String getInverse(final String relation) throws Exception {
        if (ontologies == null) {
            ontologies = OntologyLoader.loadOntologiesFromIS();
        }
        final String normalizedRelation = normalizeRelation(relation);


        try {
            return ontologies.getTerms(normalizedRelation).stream().findFirst().get().getInverseCode();
        } catch (Throwable e) {
            System.out.println("Relation not found = " + normalizedRelation);
            return "related";
        }
    }


    public static String generateIdentifier(final String pid, final String pidtype) {
        if (StringUtils.isBlank(pid) || StringUtils.isBlank(pidtype))
            throw new RuntimeException("Error pid or pidtype cannot be null");
        return DnetXsltFunctions.md5(String.format("%s::%s", pid.toLowerCase().trim(), pidtype.toLowerCase().trim()));
    }

    /**
     * This method is used only for test Scope
     *
     * @param mockInstance
     */
    public static void setInstance(final DLIUtils mockInstance) {
        instance = mockInstance;
    }

    @PostConstruct
    public void registerInstance() throws Exception {
        instance = this;
        final InputStream inputStream = this.getClass().getResourceAsStream("/eu/dnetlib/transformation/data-center.json");
        dataciteDatasource = new Gson().fromJson(IOUtils.toString(inputStream), Map.class);
    }

    public UniqueServiceLocator getServiceLocator() {
        return serviceLocator;
    }

    public void setServiceLocator(final UniqueServiceLocator serviceLocator) {
        this.serviceLocator = serviceLocator;
    }

    public Map<String, String> getDataciteDatasource() {
        return dataciteDatasource;
    }
}
