#!/usr/bin/env python3

import pytoml as toml
import mastodon
import os
import pickle
import re
import time
import trigger
import logging
import sendmail
import report

logger = logging.getLogger(__name__)


class RetootBot(object):
    def __init__(self, config):
        self.config = config
        self.client_id = self.register()
        self.m = self.login()

        # load state
        try:
            with open('seen_toots.pickle', 'rb') as f:
                self.seen_toots = pickle.load(f)
        except IOError:
            self.seen_toots = set()

    def register(self):
        client_id = os.path.join(
            'appkeys',
            self.config['mapp']['name'] +
            '@' + self.config['muser']['server']
        )

        if not os.path.isfile(client_id):
            mastodon.Mastodon.create_app(
                self.config['mapp']['name'],
                api_base_url=self.config['muser']['server'],
                to_file=client_id
            )
        return client_id

    def login(self):
        m = mastodon.Mastodon(
            client_id=self.client_id,
            api_base_url=self.config['muser']['server']
        )
        m.log_in(
            self.config['muser']['email'],
            self.config['muser']['password']
        )
        return m

    def save_last(self):
        """ save the last seen toot """
        try:
            with os.fdopen(os.open('seen_toots.pickle.part', os.O_WRONLY | os.O_EXCL | os.O_CREAT), 'wb') as f:
                pickle.dump(self.seen_toots, f)
        except FileExistsError:
            os.unlink('seen_toots.pickle.part')
            with os.fdopen(os.open('seen_toots.pickle.part', os.O_WRONLY | os.O_EXCL | os.O_CREAT), 'wb') as f:
                pickle.dump(self.seen_toots, f)
        os.rename('seen_toots.pickle.part', 'seen_toots.pickle')

    def crawl(self):
        """
        Crawl mentions from Mastodon.

        :return: list of statuses
        """
        mentions = []
        try:
            all = self.m.notifications()
        except:  # mastodon.Mastodon.MastodonAPIError is unfortunately not in __init__.py
            logger.error("Unknown Mastodon API Error.", exc_info=True)
            return mentions
        for status in all:
            if (status['type'] == 'mention' and status['status']['id'] not in self.seen_toots):
                # save state
                self.seen_toots.add(status['status']['id'])
                self.save_last()
                # add mention to mentions
                text = re.sub(r'<[^>]*>', '', status['status']['content'])
                text = re.sub("(?<=^|(?<=[^a-zA-Z0-9-_\.]))@([A-Za-z]+[A-Za-z0-9-_]+)", "", text)
                mentions.append(report.Report(status['account']['acct'],
                                              "mastodon",
                                              text,
                                              status['status']['id'],
                                              status['status']['created_at']))
        return mentions

    def repost(self, mention):
        """
        Retoots a mention.

        :param mention: (report.Report object)
        """
        logger.info('Boosting toot from %s' % (
            mention.format()))
        self.m.status_reblog(mention.id)


    def post(self, report):
        """
        Toots a report from other sources.

        :param report: (report.Report object)
        """
        toot = report.format()
        self.m.toot(toot)

    def flow(self, trigger, reports=()):
        # toot external provided messages
        for report in reports:
            self.post(report)

        # boost mentions
        retoots = []
        for mention in self.crawl():
            if not trigger.is_ok(mention.text):
                continue
            self.repost(mention)
            retoots.append(mention)

        # return mentions for mirroring
        return retoots


if __name__ == '__main__':
    # read config in TOML format (https://github.com/toml-lang/toml#toml)
    with open('config.toml') as configfile:
        config = toml.load(configfile)

    fh = logging.FileHandler(config['logging']['logpath'])
    fh.setLevel(logging.DEBUG)
    logger.addHandler(fh)

    trigger = trigger.Trigger(config)
    bot = RetootBot(config)

    try:
        while True:
            bot.flow(trigger)
            time.sleep(1)
    except KeyboardInterrupt:
            print("Good bye. Remember to restart the bot!")
    except:
        logger.error('Shutdown', exc_info=True)
        try:
            mailer = sendmail.Mailer(config)
            mailer.send('', config['mail']['contact'],
                        'Ticketfrei Crash Report',
                        attachment=config['logging']['logpath'])
        except:
            logger.error('Mail sending failed', exc_info=True)