package ru.yandex.wmconsole.notifier.handler;

import java.io.IOException;
import java.io.StringReader;
import java.util.Date;
import java.util.Map;

import javax.xml.transform.TransformerException;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Required;

import ru.yandex.wmconsole.data.LanguageEnum;
import ru.yandex.wmconsole.data.NotificationTypeEnum;
import ru.yandex.wmconsole.service.GlobalMessageService;
import ru.yandex.wmconsole.service.NotificationService;
import ru.yandex.wmconsole.util.XmlUtil;
import ru.yandex.wmtools.common.error.InternalException;

/**
 * Adds database record for new global message.
 * Sends info notification to all users.
 *
 * @author Andrey Mima (amima@yandex-team.ru)
 */
public class GlobalMessageNotificationHandler implements Handler {
    private static final Logger log = LoggerFactory.getLogger(GlobalMessageNotificationHandler.class);

    private static final String INTERNAL_HANDLE_HEADER = "header";
    private static final String INTERNAL_HANDLE_MESSAGE = "message";
    private static final String INTERNAL_MESSAGE_LANG = "message-lang";

    private static final String TAG_MESSAGE = "message";
    private static final String TAG_HEADER = "header";

    private NotificationService notificationService;
    private GlobalMessageService globalMessageService;

    @Override
    public void handleNotification(String xmlData) {
        log.debug("Global message received externally");
        GlobalMessageData globalData = handleXML(xmlData);
        try {
            internalHandleNotification(globalData.getHeader(), globalData.getMessage(), null);
        } catch (ClassCastException e) {
            log.error("ClassCastException in " + getClass().getName() + " " +
                    "while extracting notification data (possible corrupt xml)", e);
        }
    }

    @Override
    public void internalHandle(String... params) {
        if (params.length == 2) {
            internalHandleNotification(params[0], params[1], null);
        } else {
            internalHandleNotification(params[0], params[1], params[2]);
        }
    }

    public void internalHandleNotification(String header, String message, String messageLanguage) {
        log.debug("Global message received internally");
        try {
            if ((message == null) || (message.length() == 0)) {
                log.error(TAG_MESSAGE + " is null or empty (possible corrupt xml)");
                return;
            }

            if ((header == null) || (header.length() == 0)) {
                log.error(TAG_HEADER + " is null or empty (possible corrupt xml)");
                return;
            }

            Date date = new Date();
            Long issueId = fillTables(message, header, date);
            LanguageEnum lang = messageLanguage != null ? LanguageEnum.fromString(messageLanguage) : null;
            log.debug("GlobalMessageHandler: message language is " + lang);
            if (lang == null) {
                log.debug("inserting notifications for all users");
                notificationService.insertNotificationForAll(NotificationTypeEnum.GLOBAL_MESSAGE, issueId, date);
            } else {
                log.debug("inserting notifications for specific language users");
                notificationService.insertNotificationForAllWithLanguage(NotificationTypeEnum.GLOBAL_MESSAGE, issueId, date, lang);
            }
        } catch (InternalException e) {
            log.error("InternalException in " + getClass().getName() + " " + "while inserting notification(s)", e);
        }
    }

    @Override
    public void handleInternalNotification(Map<String, String> params) {
        String header = params.get(INTERNAL_HANDLE_HEADER);
        String message = params.get(INTERNAL_HANDLE_MESSAGE);
        String messageLanguage = params.get(INTERNAL_MESSAGE_LANG);
        internalHandleNotification(header, message, messageLanguage);
    }

    private Long fillTables(String message, String header, Date date) throws InternalException {
        return globalMessageService.addGlobalMessage(message, header, date);
    }

    private GlobalMessageData handleXML(String xmlData) {
        GlobalMessageData globalMessageData = new GlobalMessageData();
        StringReader stringReader = new StringReader(xmlData);
        SAXBuilder builder = new SAXBuilder();
        try {
            Document eventDocument = builder.build(stringReader);
            Element rootElement = eventDocument.getRootElement();
            Element messageElement = rootElement.getChild(TAG_MESSAGE);
            String message = null;
            try {
                message = XmlUtil.xmlToString(messageElement);
            } catch (TransformerException e) {
                log.error("TransformerException in " + getClass().getName() + " while extracting xml data", e);
                throw new IllegalArgumentException("Invalid xml notification data format");
            }
            globalMessageData.setMessage(message);

            Element headerElement = rootElement.getChild(TAG_HEADER);
            globalMessageData.setHeader(headerElement.getText());
        } catch (JDOMException e) {
            log.error("JDOMException in " + getClass().getName() + " " + "while extracting xml data", e);
            throw new IllegalArgumentException("Invalid xml notification data format");
        } catch (IOException e) {
            log.error("IOException in " + getClass().getName() + " " + "while reading xml data", e);
            throw new IllegalArgumentException("Invalid xml notification data format");
        } catch (NullPointerException e) {
            log.warn("Invalid xml notification data format", e);
            throw new IllegalArgumentException("Invalid xml notification data format");
        }

        return globalMessageData;
    }

    private static final class GlobalMessageData {
        private String message;
        private String header;

        public void setMessage(String message) {
            this.message = message;
        }

        public void setHeader(String header) {
            this.header = header;
        }

        public String getMessage() {
            return message;
        }

        public String getHeader() {
            return header;
        }
    }

    @Required
    public void setNotificationService(NotificationService notificationService) {
        this.notificationService = notificationService;
    }

    @Required
    public void setGlobalMessageService(GlobalMessageService globalMessageService) {
        this.globalMessageService = globalMessageService;
    }
}
