﻿# -*- coding: utf-8 -*-

#  This file is part of Tautulli.
#
#  Tautulli is free software: you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation, either version 3 of the License, or
#  (at your option) any later version.
#
#  Tautulli is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with Tautulli.  If not, see <http://www.gnu.org/licenses/>.

from io import open
import os
import shlex

from apscheduler.triggers.cron import CronTrigger
import email.utils

import plexpy
from plexpy import database
from plexpy import helpers
from plexpy import logger
from plexpy import newsletters


NEWSLETTER_SCHED = None


def add_newsletter_each(newsletter_id=None, notify_action=None, **kwargs):
    if not notify_action:
        logger.debug("Tautulli NewsletterHandler :: Notify called but no action received.")
        return

    data = {'newsletter': True,
            'newsletter_id': newsletter_id,
            'notify_action': notify_action}
    data.update(kwargs)
    plexpy.NOTIFY_QUEUE.put(data)


def schedule_newsletters(newsletter_id=None):
    newsletters_list = newsletters.get_newsletters(newsletter_id=newsletter_id)

    for newsletter in newsletters_list:
        newsletter_job_name = '{} (newsletter_id {})'.format(newsletter['agent_label'], newsletter['id'])

        if newsletter['active']:
            schedule_newsletter_job('newsletter-{}'.format(newsletter['id']), name=newsletter_job_name,
                                    func=add_newsletter_each, args=[newsletter['id'], 'on_cron'], cron=newsletter['cron'])
        else:
            schedule_newsletter_job('newsletter-{}'.format(newsletter['id']), name=newsletter_job_name,
                                    remove_job=True)


def schedule_newsletter_job(newsletter_job_id, name='', func=None, remove_job=False, args=None, cron=None):
    if cron:
        values = shlex.split(cron)
        # apscheduler day_of_week uses 0-6 = mon-sun
        values[4] = str((int(values[4]) - 1) % 7) if values[4].isdigit() else values[4]

    if NEWSLETTER_SCHED.get_job(newsletter_job_id):
        if remove_job:
            NEWSLETTER_SCHED.remove_job(newsletter_job_id)
            logger.info("Tautulli NewsletterHandler :: Removed scheduled newsletter: %s" % name)
        else:
            try:
                NEWSLETTER_SCHED.reschedule_job(
                    newsletter_job_id, args=args, trigger=CronTrigger(
                        minute=values[0], hour=values[1], day=values[2], month=values[3], day_of_week=values[4]
                    )
                )
                logger.info("Tautulli NewsletterHandler :: Re-scheduled newsletter: %s" % name)
            except ValueError as e:
                logger.error("Tautulli NewsletterHandler :: Failed to re-schedule newsletter: %s" % e)
    elif not remove_job:
        try:
            NEWSLETTER_SCHED.add_job(
                func, args=args, id=newsletter_job_id, trigger=CronTrigger(
                    minute=values[0], hour=values[1], day=values[2], month=values[3], day_of_week=values[4]
                ),
                misfire_grace_time=None
            )
            logger.info("Tautulli NewsletterHandler :: Scheduled newsletter: %s" % name)
        except ValueError as e:
            logger.error("Tautulli NewsletterHandler :: Failed to schedule newsletter: %s" % e)


def notify(newsletter_id=None, notify_action=None, **kwargs):
    logger.info("Tautulli NewsletterHandler :: Preparing newsletter for newsletter_id %s." % newsletter_id)

    newsletter_config = newsletters.get_newsletter_config(newsletter_id=newsletter_id)

    if not newsletter_config:
        return

    if notify_action in ('test', 'api'):
        subject = kwargs.pop('subject', None) or newsletter_config['subject']
        body = kwargs.pop('body', None) or newsletter_config['body']
        message = kwargs.pop('message', None) or newsletter_config['message']
    else:
        subject = newsletter_config['subject']
        body = newsletter_config['body']
        message = newsletter_config['message']

    email_msg_id = email.utils.make_msgid()
    email_reply_msg_id = get_last_newsletter_email_msg_id(newsletter_id=newsletter_id, notify_action=notify_action)

    newsletter_agent = newsletters.get_agent_class(newsletter_id=newsletter_id,
                                                   newsletter_id_name=newsletter_config['id_name'],
                                                   agent_id=newsletter_config['agent_id'],
                                                   config=newsletter_config['config'],
                                                   email_config=newsletter_config['email_config'],
                                                   subject=subject,
                                                   body=body,
                                                   message=message,
                                                   email_msg_id=email_msg_id,
                                                   email_reply_msg_id=email_reply_msg_id
                                                   )

    # Set the newsletter state in the db
    newsletter_log_id = set_notify_state(newsletter=newsletter_config,
                                         notify_action=notify_action,
                                         subject=newsletter_agent.subject_formatted,
                                         body=newsletter_agent.body_formatted,
                                         message=newsletter_agent.message_formatted,
                                         filename=newsletter_agent.filename_formatted,
                                         start_date=newsletter_agent.start_date.format('YYYY-MM-DD'),
                                         end_date=newsletter_agent.end_date.format('YYYY-MM-DD'),
                                         start_time=newsletter_agent.start_time,
                                         end_time=newsletter_agent.end_time,
                                         newsletter_uuid=newsletter_agent.uuid,
                                         email_msg_id=email_msg_id)

    # Send the notification
    success = newsletter_agent.send()

    if success:
        set_notify_success(newsletter_log_id)
        return True


def set_notify_state(newsletter, notify_action, subject, body, message, filename,
                     start_date, end_date, start_time, end_time, newsletter_uuid, email_msg_id):

    if newsletter and notify_action:
        db = database.MonitorDatabase()

        keys = {'timestamp': helpers.timestamp(),
                'uuid': newsletter_uuid}

        values = {'newsletter_id': newsletter['id'],
                  'agent_id': newsletter['agent_id'],
                  'agent_name': newsletter['agent_name'],
                  'notify_action': notify_action,
                  'subject_text': subject,
                  'body_text': body,
                  'message_text': message,
                  'start_date': start_date,
                  'end_date': end_date,
                  'start_time': start_time,
                  'end_time': end_time,
                  'email_msg_id': email_msg_id,
                  'filename': filename}

        db.upsert(table_name='newsletter_log', key_dict=keys, value_dict=values)
        return db.last_insert_id()
    else:
        logger.error("Tautulli NewsletterHandler :: Unable to set notify state.")


def set_notify_success(newsletter_log_id):
    keys = {'id': newsletter_log_id}
    values = {'success': 1}

    db = database.MonitorDatabase()
    db.upsert(table_name='newsletter_log', key_dict=keys, value_dict=values)


def get_last_newsletter_email_msg_id(newsletter_id, notify_action):
    db = database.MonitorDatabase()

    result = db.select_single("SELECT email_msg_id FROM newsletter_log "
                              "WHERE newsletter_id = ? AND notify_action = ? AND success = 1 "
                              "ORDER BY timestamp DESC LIMIT 1", [newsletter_id, notify_action])

    if result:
        return result['email_msg_id']


def get_newsletter(newsletter_uuid=None, newsletter_id_name=None):
    db = database.MonitorDatabase()

    if newsletter_uuid:
        result = db.select_single("SELECT start_date, end_date, uuid, filename FROM newsletter_log "
                                  "WHERE uuid = ?", [newsletter_uuid])
    elif newsletter_id_name:
        result = db.select_single("SELECT start_date, end_date, uuid, filename FROM newsletter_log "
                                  "JOIN newsletters ON newsletters.id = newsletter_log.newsletter_id "
                                  "WHERE id_name = ? AND notify_action != 'test' "
                                  "ORDER BY timestamp DESC LIMIT 1", [newsletter_id_name])
    else:
        result = None

    if result:
        newsletter_uuid = result['uuid']
        start_date = result['start_date']
        end_date = result['end_date']
        newsletter_file = result['filename'] or 'newsletter_%s-%s_%s.html' % (start_date.replace('-', ''),
                                                                              end_date.replace('-', ''),
                                                                              newsletter_uuid)

        newsletter_folder = plexpy.CONFIG.NEWSLETTER_DIR or os.path.join(plexpy.DATA_DIR, 'newsletters')
        newsletter_file_fp = os.path.join(newsletter_folder, newsletter_file)

        if newsletter_file in os.listdir(newsletter_folder):
            try:
                with open(newsletter_file_fp, 'r', encoding='utf-8') as n_file:
                    newsletter = n_file.read()
                return newsletter
            except OSError as e:
                logger.error("Tautulli NewsletterHandler :: Failed to retrieve newsletter '%s': %s" % (newsletter_uuid, e))
        else:
            logger.warn("Tautulli NewsletterHandler :: Newsletter file '%s' is missing." % newsletter_file)
