package ru.yandex.webmaster.notification;

import org.apache.commons.io.IOUtils;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Required;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.*;
import java.net.*;

public class NotifyService {

    private static final Logger log = LoggerFactory.getLogger(NotifyService.class);

    private static final int DEFAULT_TYPE = 1024;

    private String url;
    private int type = DEFAULT_TYPE;
    private int readTimeout = 100000;
    private int connectTimeout = 100000;

    @Required
    public void setUrl(final String url) {
        this.url = url;
    }

    public void setReadTimeout(int readTimeout) {
        this.readTimeout = readTimeout;
    }

    public void setConnectTimeout(int connectTimeout) {
        this.connectTimeout = connectTimeout;
    }

    public void setType(final int type) {
        this.type = type;
    }

    public boolean makeNotification(@NotNull final String host, @NotNull final String message, @NotNull final String header, final int type) {
        try {
            final URL url = makeUrl();
            final String response = postData(prepareData(host, message, header, type), url);

            Reader r = new StringReader(response);
            SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
            saxParserFactory.setNamespaceAware(true);
            saxParserFactory.setValidating(false);
            saxParserFactory.setXIncludeAware(false);

            try {
                SAXParser saxParser = saxParserFactory.newSAXParser();
                NotificationErrorHandler errorHandler = new NotificationErrorHandler();
                saxParser.parse(new InputSource(r), errorHandler);
                return errorHandler.isSuccessful();
            } catch (ParserConfigurationException e) {
                log.error("Error in xml parser configuration", e);
                return false;
            } catch (SAXException e) {
                log.error("Error in xml parsing", e);
                return false;
            }
        } catch (MalformedURLException e) {
            log.error("Problem with URL creation", e);
            return false;
        } catch (IOException e) {
            log.error("Problem with URL processing", e);
            return false;
        }
    }

    private static class NotificationErrorHandler extends DefaultHandler {
        private boolean successful = true;

        @Override
        public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
            if ("errors".equalsIgnoreCase(localName)) {
                successful = false;
            }
        }

        public boolean isSuccessful() {
            return successful;
        }
    }

    public boolean makeNotification(@NotNull final String host, @NotNull final String message, @NotNull final String header) {
        return makeNotification(host, message, header, type);
    }

    private String prepareData(final String host, final String message, final String header, final int type) {

        Document doc = new Document();
        Element notificationElement = new Element("notification");
        doc.setRootElement(notificationElement);
        notificationElement.addContent(new Element("host").setText(host));
        notificationElement.addContent(new Element("message").setText(message));
        notificationElement.addContent(new Element("header").setText(header));

        StringWriter writer = new StringWriter();
        XMLOutputter xmlOutputer = new XMLOutputter();
        xmlOutputer.setFormat(Format.getPrettyFormat());
        try {
            xmlOutputer.output(doc, writer);
        } catch (IOException e) {
            log.error("Error in xml processing", e);
        }

        String encodedData = writer.toString();
        try {
            encodedData = URLEncoder.encode(encodedData, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            log.error("Error in url encoding", e);
        }
        return "type=" + type + "&xmldata=" + encodedData;
    }

    private URL makeUrl() throws MalformedURLException {
        return new URL((url.startsWith("http://") ? url : ("http://" + url)));
    }

    private String postData(final String data, final URL endpoint) throws IOException {
        HttpURLConnection urlc = null;
        try {
            urlc = (HttpURLConnection) endpoint.openConnection();
            urlc.setRequestMethod("POST");
            urlc.setConnectTimeout(connectTimeout);
            urlc.setReadTimeout(readTimeout);
            urlc.setDoOutput(true);
            urlc.setDoInput(true);
            urlc.setUseCaches(false);
            urlc.setAllowUserInteraction(false);
            urlc.setRequestProperty("Content-Type", "application/x-www-form-urlencoded; charset=" + "UTF-8");

            final OutputStream out = urlc.getOutputStream();

            try {
                final Writer writer = new OutputStreamWriter(out, "UTF-8");
                writer.write(data);
                writer.close();
            } catch (IOException e) {
                throw e;
            } finally {
                if (out != null) {
                    out.close();
                }
            }

            final InputStream in = urlc.getInputStream();
            try {
                final String s = IOUtils.toString(in);
                in.close();
                return s;
            } catch (IOException e) {
                throw e;
            } finally {
                if (in != null) {
                    in.close();
                }
            }

        } catch (IOException e) {
            if (urlc!=null) {
                log.warn("notifier error", e);
            }
            throw e;
        } finally {
            if (urlc != null) {
                urlc.disconnect();
            }
        }
    }


}
