package eu.eudat.logic.proxy.fetching;

import com.jayway.jsonpath.Configuration;
import com.jayway.jsonpath.DocumentContext;
import com.jayway.jsonpath.spi.json.JacksonJsonProvider;
import eu.eudat.logic.proxy.config.DataUrlConfiguration;
import eu.eudat.logic.proxy.config.ExternalUrlCriteria;
import eu.eudat.logic.proxy.fetching.entities.Results;
import net.minidev.json.JSONArray;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Collectors;

public class RemoteFetcherUtils {
    private final static Logger logger = LoggerFactory.getLogger(RemoteFetcherUtils.class);

    public static Results getFromJson(DocumentContext jsonContext, DataUrlConfiguration jsonDataPath) {
        return new Results(parseData(jsonContext, jsonDataPath),
                new HashMap<>(1, 1));
    }

    public static Results getFromJsonWithRecursiveFetching(DocumentContext jsonContext, DataUrlConfiguration jsonDataPath, RemoteFetcher remoteFetcher, String requestBody, String requestType) {
        Results results = new Results(parseData(jsonContext, jsonDataPath),
                new HashMap<>(1, 1));

        List<Map<String, String>> multiResults = results.getResults().stream().map(result -> {
            ExternalUrlCriteria externalUrlCriteria = new ExternalUrlCriteria();
            externalUrlCriteria.setPath(result.get("path"));
            externalUrlCriteria.setHost(result.get("host"));
            String replacedPath = remoteFetcher.replaceCriteriaOnUrl(jsonDataPath.getUrlConfiguration().getUrl(), externalUrlCriteria, jsonDataPath.getUrlConfiguration().getFirstpage(), jsonDataPath.getUrlConfiguration().getQueries());
            return remoteFetcher.getResultsFromUrl(replacedPath, jsonDataPath.getUrlConfiguration().getData(), jsonDataPath.getUrlConfiguration().getData().getPath(), jsonDataPath.getUrlConfiguration().getContentType(), requestBody, requestType);
        }).filter(Objects::nonNull).map(results1 -> results1.getResults().get(0)).collect(Collectors.toList());
        return new Results(multiResults, new HashMap<>(1, 1));
    }

    public static Results getFromJsonWithFirstAndLastName(DocumentContext jsonContext, DataUrlConfiguration jsonDataPath) {
        Results results = new Results(parseData(jsonContext, jsonDataPath),
                new HashMap<>(1, 1));
        results.getResults().stream().forEach(entry -> {
            String name = entry.get(jsonDataPath.getFieldsUrlConfiguration().getFirstName().replace("'", "")) + " " + entry.get(jsonDataPath.getFieldsUrlConfiguration().getLastName().replace("'", ""));
            entry.put("name", name);
            entry.remove(jsonDataPath.getFieldsUrlConfiguration().getFirstName().replace("'", ""));
            entry.remove(jsonDataPath.getFieldsUrlConfiguration().getLastName().replace("'", ""));
        });
        return results;
    }

    private static List<Map<String, String>> parseData (DocumentContext jsonContext, DataUrlConfiguration jsonDataPath) {
        List <Map<String, Object>> rawData = jsonContext.read(jsonDataPath.getPath());
        List<Map<String, String>> parsedData = new ArrayList<>();
        rawData.forEach(stringObjectMap -> {
            parsedData.add(new LinkedHashMap<>());
            Arrays.stream(jsonDataPath.getFieldsUrlConfiguration().getClass().getDeclaredFields()).forEach(field -> {
                String getterMethodName = "get" + field.getName().substring(0, 1).toUpperCase(Locale.ROOT) + field.getName().substring(1);
                Method getterMethod = Arrays.stream(jsonDataPath.getFieldsUrlConfiguration().getClass().getDeclaredMethods()).filter(method -> method.getName().equals(getterMethodName)).collect(Collectors.toList()).get(0);
                try {
                    String value = ((String) getterMethod.invoke(jsonDataPath.getFieldsUrlConfiguration()));
                    if (value != null) {
                        value = value.replace("'", "");
                        if (stringObjectMap.containsKey(value)) {
                            parsedData.get(parsedData.size() - 1).put(field.getName().equals("types") ? "tags" : value, normalizeValue(stringObjectMap.get(value), (field.getName().equals("types") || field.getName().equals("uri"))));
                        }
                    }
                } catch (IllegalAccessException | InvocationTargetException e) {
                    logger.error(e.getLocalizedMessage(), e);
                }
            });
        });
        return parsedData;
    }

    private static String normalizeValue(Object value, boolean jsonString) {
        if (value instanceof JSONArray) {
            if (jsonString) {
                return ((JSONArray)value).toJSONString();
            }
            JSONArray jarr = (JSONArray) value;
            if (jarr.get(0) instanceof String) {
                return jarr.get(0).toString();
            } else {
                for (Object o : jarr) {
                    if ((o instanceof Map) && ((Map) o).containsKey("content")) {
                        return ((Map<String, String>) o).get("content");
                    }
                }
            }
        } else if (value instanceof Map) {
            return ((Map<String, String>)value).get("content");
        }
        return value != null ? value.toString() : null;
    }
}
