package eu.dnetlib.wds.utils;

import eu.dnetlib.data.transform.Ontologies;
import eu.dnetlib.data.transform.OntologyLoader;
import eu.dnetlib.enabling.locators.UniqueServiceLocator;
import eu.dnetlib.miscutils.collections.Pair;
import eu.dnetlib.miscutils.functional.xml.DnetXsltFunctions;
import eu.dnetlib.rmi.enabling.ISLookUpException;
import eu.dnetlib.rmi.enabling.ISLookUpService;
import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;

import javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class WDSUtils {

    public static Ontologies ontologies;


    public final static Map<String, Pair<String, String>> hostedByMap = new HashMap<>();
    public final static Map<String, Pair<String, String>> collectedFromMap = new HashMap<>();
    public static String HOSTED_BY_MAP_QUERY = "for $x in collection('/db/DRIVER/RepositoryServiceResources/RepositoryServiceResourceType') " +
            "where $x//FIELD/key/text()= 'archive_center'  return " +
            "concat($x//DATASOURCE_ORIGINAL_ID, '<-->', $x//FIELD[./key/text()= 'archive_center' ]/value/string(), '<-->', $x//OFFICIAL_NAME)";


    public static String COLLECTEDFROM_DATASOURCE_MAP_QUERY = "for $x in collection('/db/DRIVER/RepositoryServiceResources/RepositoryServiceResourceType')  " +
            "where $x//FIELD/key/text()!= 'archive_center'  return  concat($x//DATASOURCE_ORIGINAL_ID, '<-->', " +
            "$x//FIELD[./key/text()= 'NamespacePrefix' ]/value/string(), '<-->', $x//OFFICIAL_NAME)";

    private static WDSUtils instance;


    @Autowired
    private UniqueServiceLocator serviceLocator;

    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 normalizeRelation(final String relation) {
        if (relation == null || StringUtils.isEmpty(relation)) {
            return null;
        }
        return Character.toLowerCase(relation.charAt(0)) + relation.substring(1);

    }


    public static void generateDSMapFromQuery(final Map<String, Pair<String, String>> aMap, final String aQUERY) throws Exception {
        final ISLookUpService lookUpService = instance.getServiceLocator().getService(ISLookUpService.class);
        final List<String> hostedByList = lookUpService.quickSearchProfile(aQUERY);
        aMap.clear();
        aMap.putAll(
                hostedByList.stream()
                        .map(s -> s.split("<-->"))
                        .map(s -> new Pair<>(StringEscapeUtils.escapeXml11(s[1]), new Pair<>(s[0], s[2])))
                        .collect(Collectors.toMap(
                                Pair::getKey,
                                Pair::getValue)));
    }

    public static String getNameFromDataSourcePrefix(final String datasourcePrefix) throws Exception {
        if (collectedFromMap.keySet().size() == 0) {
            generateDSMapFromQuery(collectedFromMap, COLLECTEDFROM_DATASOURCE_MAP_QUERY);
        }
        if (!collectedFromMap.containsKey(datasourcePrefix))
            return "";
        return collectedFromMap.get(datasourcePrefix).getValue();
    }


    public static String getIdFromDataSourcePrefix(final String datasourcePrefix) throws Exception {
        if (collectedFromMap.keySet().size() == 0) {
            generateDSMapFromQuery(collectedFromMap, COLLECTEDFROM_DATASOURCE_MAP_QUERY);
        }
        if (!collectedFromMap.containsKey(datasourcePrefix))
            return "";
        return collectedFromMap.get(datasourcePrefix).getKey();
    }


    public static String getDatasourceName(final String dataCenter) throws Exception {
        if (hostedByMap.keySet().isEmpty()) {
            generateDSMapFromQuery(hostedByMap, HOSTED_BY_MAP_QUERY);
        }

        final Pair<String, String> result = hostedByMap.get(dataCenter);
        return result == null ? null : result.getValue();
    }


    public static String getDatasourceId(final String dataCenter) throws Exception {
        if (hostedByMap.keySet().isEmpty()) {
            generateDSMapFromQuery(hostedByMap, HOSTED_BY_MAP_QUERY);
        }

        final Pair<String, String> result = hostedByMap.get(dataCenter);
        return result == null ? null : result.getKey();
    }


    public static String generateWDSNsPrefix(final String input, final String prefix, final int maxLength) {
        if (StringUtils.isNotEmpty(input) && StringUtils.isNotEmpty(prefix)) {
            String cleanedString = input.replace("/", "_");
            int remainingLength = maxLength - prefix.length();
            if (cleanedString.length() > remainingLength)
                cleanedString = cleanedString.substring(0, remainingLength);
            else
                cleanedString = StringUtils.rightPad(cleanedString, remainingLength, "_");
            return prefix + cleanedString;
        }
        return null;
    }

    public static String getDOI(final String pid){
        final Pattern pattern = Pattern.compile("10\\.\\d{4,9}/[-._;()/:A-Z0-9]+$", Pattern.CASE_INSENSITIVE);
        final Matcher matcher = pattern.matcher(pid);
        if (matcher.find()) {
            return(matcher.group());

        }
        return "";
    }

    public static boolean isDate(final String date){
        //final Pattern pattern = Pattern.compile("(\\d{4})|(\\d{4}-\\d{2})|(\\d{4}-\\d{2}--\\d{4}-d{2}|(\\d{4}--\\d{4}))");
        final Pattern pattern = Pattern.compile("\\d{4}");
        final Matcher matcher = pattern.matcher(date);
        return matcher.find();
    }

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

    @PostConstruct
    public void registerInstance() throws Exception {
        instance = this;
    }


    public UniqueServiceLocator getServiceLocator() {
        return serviceLocator;
    }

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

    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()));
    }
}
