from notify.notification import Notification
from notify.service import NotificationService
from sqlalchemy import asc
from tasks import Session
from tasks.decorators import background
from tasks.Models import NotificationEmailData
import os


@background
def notify(execute_flag):
    # If True mails are sent normaly
    # If False mails are not sent. Used to avoid sending mails for jobs that
    # have been completed a long time ago. Use this if you implement notify
    # into an already existing nopaque instance. Change it to True after the
    # daemon has run one time with the flag set to False.
    # Initialize notification service
    notification_service = NotificationService(execute_flag)
    notification_service.get_smtp_configs()
    notification_service.set_server()
    # create notifications (content, recipient etc.)
    notifications = __create_mail_notifications(notification_service)
    # only login and send mails if there are any notifications
    if (len(notifications) > 0):
        try:
            notification_service.login()
            # combine new and unsent notifications
            notifications.update(notification_service.not_sent)
            # send all notifications
            __send_mail_notifications(notifications, notification_service)
            # remove unsent notifications because they have been sent now
            # but only if mail limit has not been exceeded
            if (notification_service.mail_limit_exceeded is not True):
                notification_service.not_sent = {}
            notification_service.quit()
        except Exception as e:
            notification_service.not_sent.update(notifications)
    notification_service.quit()


# Email notification functions
def __create_mail_notifications(notification_service):
    mn_session = Session()
    notification_email_data = mn_session.query(NotificationEmailData).order_by(asc(NotificationEmailData.creation_date)).all()
    notifications = {}
    for data in notification_email_data:
        notification = Notification()
        notification.set_addresses(notification_service.email_address,
                                   data.job.user.email)
        subject_template = '[nopaque] Status update for your Job/Corpora: {title}!'
        subject_template_values_dict = {'title': data.job.title}
        protocol = os.environ.get('NOPAQUE_PROTOCOL')
        domain = os.environ.get('NOPAQUE_DOMAIN')
        url = '{protocol}://{domain}/{jobs}/{id}'.format(
            protocol=protocol, domain=domain, jobs='jobs', id=data.job.id)
        body_template_values_dict = {'username': data.job.user.username,
                                     'id': data.job.id,
                                     'title': data.job.title,
                                     'status': data.notify_status,
                                     'time': data.creation_date,
                                     'url': url}
        txt_tmplt = 'notify/templates/notification_messages/notification.txt'
        html_tmplt = 'notify/templates/notification_messages/notification.html'
        notification.set_notification_content(subject_template,
                                              subject_template_values_dict,
                                              txt_tmplt,
                                              html_tmplt,
                                              body_template_values_dict)
        notifications[data.job.id] = notification
        # Using a dictionary for notifications avoids sending multiple mails
        # if the status of a job changes in a few seconds. The user will not
        # get swamped with mails for queued, running and complete if those
        # happen in in a few seconds. Only the last update will be sent.
        # This depends on the sleep time interval though.
        mn_session.delete(data)
        mn_session.commit()
        Session.remove()
    return notifications


def __send_mail_notifications(notifications, notification_service):
    for key, notification in notifications.items():
        try:
            notification_service.send(notification)
            notification_service.mail_limit_exceeded = False
        except Exception as e:
            # Adds notifications to unsent if mail server exceded limit for
            # consecutive mail sending
            notification_service.not_sent[key] = notification
            notification_service.mail_limit_exceeded = True