#!/usr/bin/env python3
"""
Allows etext-specific messages to be sent to an address registered for that etext.

uses a json file to save notification addresses.


"""

from datetime import datetime
import json
import os
import re
import stat
import smtplib

from email.message import EmailMessage

from libgutenberg.Logger import debug, error, info, warning # pylint: disable=unused-import


from ebookmaker.CommonCode import NOTIFICATION_DIR

PRIVATE = os.getenv('PRIVATE') or ''
NOTIFY_FILE = os.path.join(PRIVATE, 'logs', 'json', 'notify.json')
ARCHIVE_DIR = os.path.join(PRIVATE,'logs', 'json', 'backup')
SMTP_HOST = os.getenv('SMTP_HOST') or 'localhost'
SMTP_USER = os.getenv('SMTP_USER') or ''
SMTP_PASSWORD = os.getenv('SMTP_PASSWORD') or ''
REPLY_TO_EMAIL = os.getenv('REPLY_TO_EMAIL') or 'help2021@pglaf.org'
SMTP_SSL = os.getenv('SMTP_SSL') or False

NOTIFY_PROCESSED = """
Ebook #{ebook} has been processed and ebook files have been posted successfully at Project Gutenberg.
"""
NOTIFY_ISSUE = """
Ebook #{ebook} has been processed and ebook files have been posted at Project Gutenberg. The posting team has been notified of a possible issue with the processing. You will be notified again when the issue is resolved.
"""
NOTIFY_CRITICAL = """
Ebook #{ebook} has been processed and posted at Project Gutenberg. There may exist a critical issue with the ebook processing. Here is the log entry:
{records}

The full processing log for this ebook may be downloaded at https://www.gutenberg.org/cache/epub/{ebook}/pg{ebook}.converter.log
"""


class AddressBook:
    """
        address_book is a dict of dicts, one dict per book.
        each book has email addresses by role, default role is 'notify', usually the submitter.
        other roles are 'ww' for the white washer managing the submission.
        and other roles that may be invented in the future
    """
    def __init__(self):
        self._address_book = None
        
    @property
    def address_book(self):
        if self._address_book == None:
            try:
                with open(NOTIFY_FILE, 'r') as json_file:
                    self._address_book = json.loads(json_file.read())
            except FileNotFoundError:
                error('could not find %s', NOTIFY_FILE)
                self._address_book = {}
                self.save()
        return self._address_book

    def set_email(self, ebook, email, role='notify'):
        if not email:
            return False
        try:
            ebook = str(int(ebook))
        except ValueError:
            return False
        if ebook in self.address_book:
            addresses = self.address_book[ebook]
        else:
            addresses = {}
            self.address_book[ebook] = addresses
        if email and not "@" in email:
            email = email.strip() + "@pglaf.org"
        addresses[role] = email
        self.save()
        return True

    def get_email(self, ebook, role='notify'):
        try:
            ebook = str(int(ebook))
        except ValueError:
            return ''
        return self.address_book.get(ebook, {}).get(role, '')

    def save(self):
        """ save the data before destroying the object """
        with open(NOTIFY_FILE, 'w+') as json_file:
            json.dump(self.address_book, json_file)


ADDRESS_BOOK = AddressBook()

def notify(ebook, message, role='notify', subject='Gutenberg backend notifications'):
    info(message)
    address = ADDRESS_BOOK.get_email(ebook, role=role)
    if not address:
        # archive unsent message
        message_archive = '{}/{}.messages'.format(ARCHIVE_DIR, ebook)
        now = datetime.now().isoformat()
        with open(message_archive, 'a+') as messagefile:
            messagefile.write('%s: %s\nSubject: %s\n%s\n' % (role, now, subject, message))
        return 1
    try:
        if SMTP_SSL:
            server = smtplib.SMTP_SSL(SMTP_HOST)
        else:
            server = smtplib.SMTP(SMTP_HOST)
        if SMTP_PASSWORD:
            server.login(SMTP_USER, SMTP_PASSWORD)
        msg = EmailMessage()
        msg.set_content(message)
        msg['Subject'] = subject
        msg['From'] = REPLY_TO_EMAIL
        msg['To'] = address
        server.send_message(msg)
        server.quit()
        return 2
    except ConnectionError as e:
        error(e)
        return 0


def send_notifications(ebooks=None):
    """ critical notifications are generated by ebookconverter logging
        ebooks should be a list of processed ebooks.
    """
    ebooks = ebooks if ebooks else []
    criticals = []
    for filename in sorted(os.listdir(NOTIFICATION_DIR)):
        mode = os.stat(os.path.join(NOTIFICATION_DIR, filename))[stat.ST_MODE]
        if stat.S_ISDIR(mode):
            continue
        m = re.match(r'^(\d+)\.txt$', filename)
        if m:
            ebook = int(m.group(1))
            criticals.append(ebook)
            queued_file = os.path.join(NOTIFICATION_DIR, filename)
            records = ''
            with open(queued_file, 'r') as messagefile:
                records = messagefile.read()
            if records:
                message = NOTIFY_CRITICAL.format(ebook=ebook, records=records)
                code = notify(ebook, message, role='ww')
                if code:
                    os.remove(queued_file)

    # send 'processed' notifications to notification address
    for ebook in ebooks:
        try:
            ebook = int(ebook)
        except ValueError:
            continue
        if ebook in criticals:
            message = NOTIFY_ISSUE.format(ebook=ebook)
        else:
            message = NOTIFY_PROCESSED.format(ebook=ebook)
        notify(ebook, message)
