package ru.yandex.wmconsole.notifier.handler;

import java.io.IOException;
import java.io.StringReader;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

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 ru.yandex.wmconsole.data.NotificationTypeEnum;
import ru.yandex.wmconsole.service.DoorwayNotificationService;
import ru.yandex.wmconsole.service.HostInfoService;
import ru.yandex.wmconsole.service.NotificationService;
import ru.yandex.wmconsole.service.UsersHostsService;
import ru.yandex.wmtools.common.SupportedProtocols;
import ru.yandex.wmtools.common.error.InternalException;

/**
 * User: Alexey Zakharov <azakharov@yandex-team.ru>
 * Date: 20.02.12
*/
public class DoorwayNotificationHandler implements Handler {
    private static final Logger log = LoggerFactory.getLogger(DoorwayNotificationHandler.class);

    private static final String TAG_URL = "url";

    private NotificationService notificationService;
    private DoorwayNotificationService doorwayNotificationService;
    private UsersHostsService usersHostsService;
    private HostInfoService hostInfoService;

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

    public void setDoorwayNotificationService(DoorwayNotificationService doorwayNotificationService) {
        this.doorwayNotificationService = doorwayNotificationService;
    }

    public void setUsersHostsService(UsersHostsService usersHostsService) {
        this.usersHostsService = usersHostsService;
    }

    public void setHostInfoService(HostInfoService hostInfoService) {
        this.hostInfoService = hostInfoService;
    }

    @Override
    @SuppressWarnings("unchecked")
    public void handleNotification(final String xmlData) {
        log.debug("Doorway notifications received externally");
        final List<String> urlStringList = handleXML(xmlData);
        try {
            if ((urlStringList == null) || (urlStringList.size() == 0)) {
                log.error(TAG_URL + " is null or empty (possible corrupt xml)");
                return;
            }

            for (String urlString : urlStringList) {
                try {
                    final URL url = SupportedProtocols.getURL(urlString);
                    final String hostName = url.getHost();
                    final Long hostId = getHostId(hostName);

                    if (hostId == null) {
                        log.warn("Host with name " + hostName + " was not found in database");
                        continue;
                    }

                    final Date date = new Date();
                    final Long issueId = doorwayNotificationService.addDoorwayNotification(urlString, hostId, date);

                    final List<Long> userIds = getHostUsers(hostId);
                    notificationService.insertNotificationForUsers(
                            NotificationTypeEnum.DOORWAY, issueId, userIds, date);
                } catch (InternalException e) {
                    log.error("InternalException in " + getClass().getName() + " " +
                            "while inserting notification(s)", e);
                } catch (MalformedURLException e) {
                    log.error("MalformedURLException in " + getClass().getName() + " " +
                            "while inserting notification(s)", e);
                } catch (URISyntaxException e) {
                    log.error("URISyntaxException in " + getClass().getName() + " " +
                            "while inserting notification(s)", e);
                } catch (SupportedProtocols.UnsupportedProtocolException e) {
                    log.error("SupportedProtocols.UnsupportedProtocolException in " + getClass().getName() + " " +
                            "while inserting notification(s)", e);
                }
            }
        } catch (ClassCastException e) {
            log.error("ClassCastException in " + getClass().getName() + " " +
                    "while extracting notification data (possible corrupt xml)", e);
        }
    }

    @Override
    public void internalHandle(String... params) {
        throw new UnsupportedOperationException("Internal handle not supported for Doorway notification");
    }

    @Override
    public void handleInternalNotification(Map<String, String> params) {
        throw new UnsupportedOperationException("Internal handle not supported for Doorway notification");
    }


    private Long getHostId(String hostName) throws InternalException {
        return hostInfoService.getHostIdByHostName(hostName, false);
    }

    private List<Long> getHostUsers(Long hostId) throws InternalException {
        return usersHostsService.getHostVerifiedUserIds(hostId);
    }

    private List<String> handleXML(String xmlData) {
        final List<String> urlStringList = new ArrayList<String>();
        final StringReader stringReader = new StringReader(xmlData);
        final SAXBuilder builder = new SAXBuilder();
        try {
            final Document eventDocument = builder.build(stringReader);
            final Element rootElement = eventDocument.getRootElement();
            final List urlElementList = rootElement.getChildren(TAG_URL);
            for (Object url : urlElementList) {
                urlStringList.add(((Element) url).getText());
            }
            log.debug(" url list size is " + urlStringList.size());
        } 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 urlStringList;
    }
}
