package ru.yandex.wmconsole.periodic;

import java.io.UnsupportedEncodingException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MailDateFormat;
import javax.mail.internet.MimeMessage;

import org.joda.time.DateMidnight;
import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.mail.MailException;

import ru.yandex.common.scheduler.ExecutionContext;
import ru.yandex.wmconsole.data.partition.WMCPartition;
import ru.yandex.wmconsole.notifier.sender.EmailNotificationSender;
import ru.yandex.wmconsole.service.dao.TblConfigDao;
import ru.yandex.wmconsole.service.dao.TblHostmonMonitoringDao;
import ru.yandex.wmtools.common.error.InternalException;
import ru.yandex.wmtools.common.error.InternalProblem;
import ru.yandex.wmtools.common.service.MailService;
import ru.yandex.wmtools.common.util.scheduler.timetable.AbstractTaskExecutor;

/**
 * Мониторинг наличия данных в нашей базе, загруженных от хостмона
 *
 * User: azakharov
 * Date: 19.06.13
 * Time: 20:34
 */
public class HostMonMonitoringTask extends AbstractTaskExecutor {

    private static final String SELECT_UPDATED_HOSTS_COUNT_QUERY =
            "SELECT " +
                    "   COUNT(distinct host_id) " +
                    "FROM " +
                    "   tbl_hostmon_stats%03d " +
                    "WHERE " +
                    "   time_stamp >= ? " +
                    "AND " +
                    "   time_stamp < ? ";

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

    private DateFormat dateFormat = new SimpleDateFormat("dd.MM.yy");

    private MailService mailService;
    private TblHostmonMonitoringDao tblHostmonMonitoringDao;
    private TblConfigDao tblConfigDao;

    private String fromEmail;
    private String fromName;
    private String toEmail;
    private String toName;

    private String subject;
    private String textTemplate;

    @Override
    public String runWithRELogging(ExecutionContext context) throws InternalException {
        DateTime now = new DateTime();
        DateMidnight todayMidnight = now.toDateMidnight();
        DateMidnight yesterdayMidnight = todayMidnight.minusDays(1);
        DateMidnight twoDaysAgo = todayMidnight.minusDays(2);

        long count = 0L;
        for (int i = 0; i < getDatabaseCount()-1; i++) {
            for (int p = 0; p < 512; p++) {
                final String query = String.format(SELECT_UPDATED_HOSTS_COUNT_QUERY, p);
                long l = getJdbcTemplate(new WMCPartition(i)).queryForLong(
                        query, yesterdayMidnight.toDate(), todayMidnight.toDate());
                count += l;
            }
        }

        log.debug("Hosts count from " + yesterdayMidnight + " to " + todayMidnight + " is " + count);
        tblHostmonMonitoringDao.saveHostsCount(yesterdayMidnight.toDate(), count);

        final Long prevCount = tblHostmonMonitoringDao.getHostsCount(twoDaysAgo.toDate());
        if (prevCount == null) {
            log.warn("Hosts count for " + twoDaysAgo.toDate() + " not found.");
            return "Previous count not found";
        }

        final Double emailThreshold = tblConfigDao.getConfigDoubleValue("hostmon.email.threshold");

        if (prevCount > 0) {
            double fraction = ((double)(prevCount - count)) / (double)prevCount;
            if (fraction > emailThreshold) {
                try {
                    sendMonitoringEmail(fraction, yesterdayMidnight.toDate(), twoDaysAgo.toDate());
                } catch (InternalException e) {
                    log.error(e.getMessage(), e);
                    return e.getMessage();
                }
            }
        }

        return HostMonMonitoringTask.class.getSimpleName() + " done";
    }

    private void sendMonitoringEmail(final double fraction, final Date yesterday, final Date twoDaysAgo) throws InternalException {
        final String emailEncoding = "UTF-8";
        MimeMessage mail = mailService.getMailSender().createMimeMessage();
        final InternetAddress from;
        try {
            from = new InternetAddress(fromEmail, fromName, emailEncoding);
            mail.setFrom(from);
            mail.setRecipient(Message.RecipientType.TO, new InternetAddress(toEmail, toName, emailEncoding));
            mail.setSubject(subject, emailEncoding);
            String text = String.format(
                    textTemplate,
                    dateFormat.format(yesterday),
                    fraction * 100.0f,
                    dateFormat.format(twoDaysAgo));
            mail.setText(text, emailEncoding);
            final Date date = new Date();
            mail.setSentDate(date);
            final MailDateFormat mdf = new MailDateFormat();
            final String sentDate = mdf.format(date);
            final String xYandexService = mailService.getYandexServiceHeaderValue(sentDate,
                    from.toUnicodeString(),
                    subject,
                    EmailNotificationSender.YAWEBMASTER);
            log.debug(sentDate + "_" + from.toUnicodeString() + "_" + subject + "_" + EmailNotificationSender.YAWEBMASTER + "_76336c7a96b13f0258054d239e70fec8");
            log.debug("X-Yandex-Service: " + xYandexService);
            mail.addHeader("X-Yandex-Service", xYandexService);
        } catch (UnsupportedEncodingException e) {
            final String message = "UnsupportedEncodingException while sending email";
            throw new InternalException(InternalProblem.MAIL_SEND_FAILURE, message);
        } catch (MessagingException e) {
            final String message = "MessagingException while sending email";
            throw new InternalException(InternalProblem.MAIL_SEND_FAILURE, message);
        }

        try {
            log.info("Sending monitoring email to " + toEmail);
            mailService.getMailSender().send(mail);
        } catch (MailException e) {
            final String message = "MailException in " + getClass().getName();
            throw new InternalException(InternalProblem.MAIL_SEND_FAILURE, message);
        }
    }

    @Required
    public void setMailService(MailService mailService) {
        this.mailService = mailService;
    }

    @Required
    public void setFromEmail(String fromEmail) {
        this.fromEmail = fromEmail;
    }

    @Required
    public void setFromName(String fromName) {
        this.fromName = fromName;
    }

    @Required
    public void setToEmail(String toEmail) {
        this.toEmail = toEmail;
    }

    @Required
    public void setToName(String toName) {
        this.toName = toName;
    }

    @Required
    public void setTblHostmonMonitoringDao(TblHostmonMonitoringDao tblHostmonMonitoringDao) {
        this.tblHostmonMonitoringDao = tblHostmonMonitoringDao;
    }

    @Required
    public void setTblConfigDao(TblConfigDao tblConfigDao) {
        this.tblConfigDao = tblConfigDao;
    }

    @Required
    public void setSubject(String subject) {
        this.subject = subject;
    }

    @Required
    public void setTextTemplate(String textTemplate) {
        this.textTemplate = textTemplate;
    }
}
