package eu.eudat.logic.proxy.config.configloaders;

import com.fasterxml.jackson.databind.ObjectMapper;
import eu.eudat.logic.proxy.config.ExternalUrls;
import eu.eudat.logic.security.customproviders.ConfigurableProvider.entities.ConfigurableProviders;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Profile;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import java.io.*;
import java.net.URL;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/*
@Service("configLoader")
@Profile({ "production", "staging" })
public class ProductionConfigLoader implements ConfigLoader {
    private static final Logger logger = LoggerFactory.getLogger(ProductionConfigLoader.class);

    private ExternalUrls externalUrls;
    private List<String> rdaProperties;
    private XWPFDocument document;
    private ConfigurableProviders configurableProviders;
    private Map<String, String> keyToSourceMap;

    @Autowired
    private Environment environment;

    private void setExternalUrls() {
        String fileUrl = this.environment.getProperty("configuration.externalUrls");
        logger.info("Loaded also config file: " + fileUrl);
        String current = null;
        InputStream is = null;
        try {
            current = new java.io.File(".").getCanonicalPath();
            JAXBContext jaxbContext = JAXBContext.newInstance(ExternalUrls.class);
            Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
            is = new URL(Paths.get(fileUrl).toUri().toURL().toString()).openStream();
            externalUrls = (ExternalUrls) jaxbUnmarshaller.unmarshal(is);

        } catch (Exception ex) {
            logger.error("Cannot find in folder" + current, ex);
        } finally {
            try {
                if (is != null) is.close();
            } catch (IOException e) {
                logger.warn("Warning: Could not close a stream after reading from file: " + fileUrl, e);
            }
        }
    }

    private void setRdaProperties() {
        String filePath = environment.getProperty("configuration.rda");
        BufferedReader reader;
        List<String> rdaList = new LinkedList<>();
        for (int i = 0; i < 2; i++) {
            try {
                if (i == 0) {
                    reader = new BufferedReader(new FileReader(filePath));
                } else {
                    reader = new BufferedReader(new FileReader(getClass().getClassLoader().getResource(filePath).getFile()));
                }
                String line = reader.readLine();
                while (line != null) {
                    rdaList.add(line);
                    line = reader.readLine();
                }
                reader.close();
                break;
            } catch (IOException e) {
                if (i == 1) {
                    logger.error(e.getMessage(), e);
                }
            }
        }

        rdaProperties = rdaList;
    }

    private void setDocument() {
        String filePath = environment.getProperty("configuration.h2020template");
        InputStream is = null;
        for (int i = 0; i < 2; i++) {
            try {
                if (i == 0) {
                    is = new URL(Paths.get(filePath).toUri().toURL().toString()).openStream();
                } else {
                    is = getClass().getClassLoader().getResource(filePath).openStream();
                }
                this.document = new XWPFDocument(is);
                is.close();
                is = null;
                break;
            } catch (IOException e) {
                logger.error(e.getMessage(), e);
            } finally {
                try {
                    if (is != null) is.close();
                } catch (IOException e) {
                    logger.warn("Warning: Could not close a stream after reading from file: " + filePath, e);
                }
            }
        }
    }

    private void setConfigurableProviders() {
        String filePath = environment.getProperty("configuration.configurable_login_providers");
        InputStream is = null;
        try {
            File tempFile = new File(filePath);
            if (tempFile.exists()) {
                is = new URL(Paths.get(filePath).toUri().toURL().toString()).openStream();
            } else {
                is = getClass().getClassLoader().getResource(filePath).openStream();
            }
            ObjectMapper objectMapper = new ObjectMapper();
            this.configurableProviders = objectMapper.readValue(is, ConfigurableProviders.class);
        } catch (IOException e) {
            logger.error(e.getMessage(), e);
        } finally {
            try {
                if (is != null) is.close();
            } catch (IOException e) {
               logger.warn("Warning: Could not close a stream after reading from file: " + filePath, e);
            }
        }
    }
    private void setKeyToSourceMap() {
        String filePath = this.environment.getProperty("configuration.externalUrls");
        logger.info("Loaded also config file: " + filePath);
        Document doc = getXmlDocumentFromFilePath(filePath);
        if (doc == null) {
            this.keyToSourceMap = null;
            return;
        }
        String xpathExpression = "//key";
        Map<String, String> keysToSourceMap = new HashMap<>();
        List<String> keys = getXmlValuesFromXPath(doc, xpathExpression);
        keys = keys.stream().distinct().collect(Collectors.toList());
        for (String key : keys) {
            String sourceExpression = String.format("//urlConfig[key='%s']/label", key);
            List<String> sources = getXmlValuesFromXPath(doc, sourceExpression);
            if (sources.size() != 0) {
                keysToSourceMap.put(key, sources.get(0));
            }
        }
        this.keyToSourceMap = keysToSourceMap;
    }

    public ExternalUrls getExternalUrls() {
        this.setExternalUrls();
        return externalUrls;
    }

    public List<String> getRdaProperties() {
        this.setRdaProperties();
        return rdaProperties;
    }

    public XWPFDocument getDocument() {
        this.setDocument();
        return document;
    }

    public ConfigurableProviders getConfigurableProviders() {
        this.setConfigurableProviders();
        return configurableProviders;
    }

    public Map<String, String> getKeyToSourceMap() {
        this.setKeyToSourceMap();
        return keyToSourceMap;
    }

    private Document getXmlDocumentFromFilePath(String filePath) {
        InputStream is = null;
        Document doc;
        try {
            System.out.println(filePath);
            is = new URL(Paths.get(filePath).toUri().toURL().toString()).openStream();
            DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
            doc = documentBuilder.parse(is);
            return doc;
        } catch (IOException | ParserConfigurationException | SAXException e) {
            logger.error(e.getMessage(), e);
        } finally {
            try {
                if (is != null) {
                    is.close();
                }
            } catch (IOException e) {
                logger.warn("Warning: Could not close a stream after reading from file: " + filePath, e);
            }
        }
        return null;
    }

    private List<String> getXmlValuesFromXPath(Document doc, String expression) {
        XPath xPath = XPathFactory.newInstance().newXPath();
        NodeList nodeList = null;
        List<String> values = new LinkedList<>();
        try {
            nodeList = (NodeList) xPath.compile(expression).evaluate(doc, XPathConstants.NODESET);
        } catch (XPathExpressionException e) {
            logger.error(e.getMessage(), e);
        }
        if (nodeList != null) {
            for (int i = 0; i < nodeList.getLength(); i++) {
                Node node = nodeList.item(i);
                if (node.hasChildNodes()) {
                    values.add(nodeList.item(i).getChildNodes().item(0).getNodeValue());
                }
            }
        }
        return values;
    }
}*/
