package eu.dnetlib.data.search.app;

import eu.dnetlib.api.enabling.ISLookUpService;
import eu.dnetlib.api.enabling.ISRegistryService;
import eu.dnetlib.enabling.tools.blackboard.BlackboardJob;
import eu.dnetlib.enabling.tools.blackboard.BlackboardNotificationHandler;
import eu.dnetlib.enabling.tools.blackboard.BlackboardServerHandler;
import gr.uoa.di.driver.util.ServiceLocator;
import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.*;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;

public class SearchServiceBlackboardHandler extends
        BlackboardNotificationHandler<BlackboardServerHandler> {

    private static Logger logger = Logger.getLogger(SearchServiceBlackboardHandler.class);

    private SearchServiceImpl searchService = null;
    private ServiceLocator<ISLookUpService> lookUpServiceServiceLocator = null;
    private ServiceLocator<ISRegistryService> registryServiceServiceLocator = null;

    private XPathExpression indexMdFormatExpression = null;
    private XPathExpression indexLayoutExpression = null;
    private XPathExpression searchMdFormatExpression = null;


    public SearchServiceBlackboardHandler() throws XPathExpressionException {
        XPathFactory factory = XPathFactory.newInstance();
        XPath xpath = factory.newXPath();

        indexMdFormatExpression = xpath.compile("//METADATA_FORMAT");
        indexLayoutExpression = xpath.compile("//METADATA_FORMAT_LAYOUT");
        searchMdFormatExpression = xpath.compile("//SERVICE_PROPERTIES/PROPERTY[@key='mdformat']");
    }

    @Override
    protected void processJob(BlackboardJob job) {
        String action = job.getAction();

        try {
			getBlackboardHandler().ongoing(job);

            if (action.equals("UpdateIndex")) {
                String indexProfileId = job.getParameters().get("IndexId");
                logger.info("Updating the service to use index " + indexProfileId);

                String indexProfile = lookUpServiceServiceLocator.getService().getResourceProfile(indexProfileId);
                String mdFormat = getMDFormat(indexProfile);
                String indexLayout = getIndexLayout(indexProfile);
                logger.info("New mdFormat: " + mdFormat + ", new layout: " + indexLayout);

                searchService.setMdFormat(mdFormat);
                searchService.setIndexLayout(indexLayout);

                String searchProfileId = searchService.getServiceEPR().getParameter("serviceId");

                updateSearchServiceProfile(searchProfileId, mdFormat);

            } else {
                throw new Exception("Don't know what to do with " + action);
            }

			getBlackboardHandler().done(job);
        } catch (Exception e) {
			getBlackboardHandler().failed(job, e);

            logger.error("Error processing job", e);
        }
    }

    private String getIndexLayout(String indexProfile) throws ParserConfigurationException, IOException, SAXException, XPathExpressionException {
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        dbf.setNamespaceAware(true);
        DocumentBuilder db = dbf.newDocumentBuilder();
        Document doc = db.parse(new InputSource(new StringReader(indexProfile)));

        Node layoutNode = (Node) indexLayoutExpression.evaluate(doc, XPathConstants.NODE);

        return layoutNode.getTextContent();
    }

    private String getMDFormat(String indexProfile) throws ParserConfigurationException, IOException, SAXException, XPathExpressionException {
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        dbf.setNamespaceAware(true);
        DocumentBuilder db = dbf.newDocumentBuilder();
        Document doc = db.parse(new InputSource(new StringReader(indexProfile)));

        Node layoutNode = (Node) indexMdFormatExpression.evaluate(doc, XPathConstants.NODE);

        return layoutNode.getTextContent();
    }

    private void updateSearchServiceProfile(String searchProfileId, String mdFormat)  {
        try {
            String searchProfile = lookUpServiceServiceLocator.getService().getResourceProfile(searchProfileId);

            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            dbf.setNamespaceAware(true);
            DocumentBuilder db = dbf.newDocumentBuilder();
            Document doc = db.parse(new InputSource(new StringReader(searchProfile)));


            Node node = (Node) searchMdFormatExpression.evaluate(doc,XPathConstants.NODE);
            node.getAttributes().getNamedItem("value").setNodeValue(mdFormat);

            DOMSource domSource = new DOMSource(doc);
            StringWriter writer = new StringWriter();
            StreamResult result = new StreamResult(writer);
            TransformerFactory tf = TransformerFactory.newInstance();
            Transformer transformer = tf.newTransformer();
            transformer.transform(domSource, result);

            registryServiceServiceLocator.getService().updateProfile(searchProfileId, writer.toString(), "SearchServiceResourceType");

        } catch (Exception e) {
            logger.error("Fail to update search service profile with id " + searchProfileId, e);
        }

        logger.info("Updated search service profile with id " +searchProfileId);

    }


    public ServiceLocator<ISLookUpService> getLookUpServiceServiceLocator() {
        return lookUpServiceServiceLocator;
    }

    public void setLookUpServiceServiceLocator(ServiceLocator<ISLookUpService> lookUpServiceServiceLocator) {
        this.lookUpServiceServiceLocator = lookUpServiceServiceLocator;
    }

    public void setRegistryServiceServiceLocator(ServiceLocator<ISRegistryService> registryServiceServiceLocator) {
        this.registryServiceServiceLocator = registryServiceServiceLocator;
    }

    public SearchServiceImpl getSearchService() {
        return searchService;
    }

    public void setSearchService(SearchServiceImpl searchService) {
        this.searchService = searchService;
    }
}
