package eu.eudat.logic.proxy.fetching;

import com.fasterxml.jackson.databind.ObjectMapper;
import eu.eudat.logic.proxy.fetching.entities.Config;
import eu.eudat.logic.proxy.fetching.entities.ConfigSingle;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Component;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.*;

@Component
public class LocalFetcher {

	@Cacheable("currencies")
	public List<Map<String, String>> retrieveCurrency() throws Exception {
		InputStream is = getClass().getClassLoader().getResource("internal/fetchConfig.xml").openStream();
		JAXBContext context = JAXBContext.newInstance(Config.class);
		Unmarshaller unmarshaller = context.createUnmarshaller();
		Config config = (Config) unmarshaller.unmarshal(is);
		is.close();
		ConfigSingle currencyConfig = config.getConfigs().stream().filter(configSingle -> configSingle.getType().equals("currency")).findFirst().get();

		return retrieveData(currencyConfig);
	}

	public List<Map<String, String>> retrieveData(ConfigSingle configSingle) throws Exception {
		List<Map<String, String>> result = new ArrayList<>();
		InputStream is = getClass().getClassLoader().getResource(configSingle.getFilePath()).openStream();
		FileType type = FileType.fromName(configSingle.getFileType());
		switch(type) {
			case XML:
			{
				Class<?> aClass = Class.forName(configSingle.getParseClass());
				JAXBContext context = JAXBContext.newInstance(aClass);
				Unmarshaller unmarshaller = context.createUnmarshaller();

				Object object = unmarshaller.unmarshal(is);
				is.close();
				Method reader = null;
				if (configSingle.getParseField() != null && !configSingle.getParseField().isEmpty()) {
					String camelCaseGetter =configSingle.getParseField() != null && !configSingle.getParseField().isEmpty() ? "get" + configSingle.getParseField().substring(0, 1).toUpperCase() + configSingle.getParseField().substring(1) : "";
					reader = aClass.getMethod(camelCaseGetter);
				}
				ObjectMapper objectMapper = new ObjectMapper();
				List<Map<String, String>> values = new ArrayList<>();
				int max = 1;
				if (reader != null) {
					Object invokedField = reader.invoke(object);
					if (invokedField instanceof Collection) {
						max = ((Collection) invokedField).size();
					}
				}
				for (int i = 0; i< max; i++) {
					Object value;
					if (reader != null) {
						Object invokedField = reader.invoke(object);
						if (invokedField instanceof Collection) {
							value = ((Collection) invokedField).toArray()[i];
						} else {
							value = invokedField;
						}
					} else {
						value = object;
					}
					Map<String, String> map = objectMapper.convertValue(value, Map.class);
					result.add(new HashMap<>());
					result.get(result.size() - 1).put("name", map.get(configSingle.getName()));
					result.get(result.size() - 1).put("value", map.get(configSingle.getValue()));
				}
			}
		}
		return result;
	}

	public enum FileType {
		XML("xml"), JSON("json");
		private String name;

		FileType(String name) {
			this.name = name;
		}

		public String getName() {
			return name;
		}

		public static FileType fromName(String name) {
			for (FileType type: FileType.values()) {
				if (name.equals(type.getName())) {
					return type;
				}
			}
			throw new NoSuchElementException("File Type [" + name + "] is not supported");
		}
	}
}
