#!/usr/bin/env python3
# By: Riasat Ullah

# This class checks to see if a trial period has ended or not.

import sys
sys.path.append('/var/www/html/taskcallrest/')

from dbqueries import db_accounts
from modules import subscription_updater
from notices import account_notices
from utils import constants, helpers, informational_content, key_manager, logging, mail, permissions, times, var_names
from utils.db_connection import CONN_POOL, CACHE_CLIENT
import argparse
import configuration as configs
import datetime


class TrialPeriodMonitor(object):

    def __init__(self, conn, cache_client, timestamp):
        self.conn = conn
        self.cache_client = cache_client
        self.timestamp = timestamp

    def monitor(self):
        try:
            org_ids = db_accounts.get_org_ids_of_concluded_trials(self.conn, self.timestamp)
            org_cards = db_accounts.get_default_cards(self.conn, self.timestamp, org_ids)

            org_ids_to_retain_subscriptions_for = list(org_cards.keys())
            org_ids_to_end_trials_for = list(set(org_ids).difference(set(org_ids_to_retain_subscriptions_for)))

            if len(org_ids) == 0:
                logging.info('No organizations with concluded trials. Nothing to update.')
            else:
                logging.info('Found organizations with concluded trials - ' + str(org_ids))

                digital_org_perm = permissions.create_digital_operations_org_permission()
                org_perm_map = dict()
                for id_ in org_ids_to_end_trials_for:
                    org_perm_map[id_] = digital_org_perm
                subscription_updater.update_subscription(self.conn, self.cache_client, self.timestamp,
                                                         configs.free_subscription_id, org_perm_map)

                org_perm_map = dict()
                for id_ in org_ids_to_retain_subscriptions_for:
                    org_perm_map[id_] = digital_org_perm
                subscription_updater.update_subscription(self.conn, self.cache_client, self.timestamp,
                                                         configs.digital_operations_subscription_id, org_perm_map)

                logging.info('Updated subscription to the Free subscription package.')
        except RuntimeError as e:
            logging.error(str(e))

    def notify(self):
        try:
            owner_det = db_accounts.get_organization_owner_info_of_almost_ending_trials(
                self.conn, self.timestamp, [1, 3])

            if len(owner_det) == 0:
                logging.info('No organizations were found whose trial is close to expiry. No notifications sent.')
            else:
                logging.info('Found organizations whose trial is about to expire - ' +
                             str([x[var_names.organization_id] for x in owner_det]))
                for item in owner_det:
                    logging.info('Emailing ' + item[var_names.email] + ' for org ID ' +
                                 str(item[var_names.organization_id]))
                    subject, message = account_notices.trial_ending_email_content(
                        item[var_names.days_buffer], item[var_names.first_name])
                    mail.AmazonSesDispatcher(subject, message, item[var_names.email]).start()
        except RuntimeError as e:
            logging.error(str(e))

    def send_onboarding_emails(self, overlook_period=None):
        '''
        Finds details of all the users who should receive onboarding emails and emails them accordingly.
        :param overlook_period: (tuple) -> (start date, end date) period to ignore
        '''
        eligible_details = db_accounts.details_for_onboarding_emails(self.conn, self.timestamp)
        if len(eligible_details) == 0:
            logging.info('No organizations found for sending onboarding emails to.')
        else:
            logging.info('Found organizations for onboarding emails. Checking if any should be sent out today...')
            for item in eligible_details:
                for user_det in item[var_names.users]:
                    recipient_id = key_manager.encode_general_identifier(
                        [item[var_names.account_id], user_det[var_names.preferred_username]]
                    )
                    subject, message = self.pick_onboarding_email(
                        user_det[var_names.start_period], item[var_names.answers],
                        item[var_names.component_features], recipient_id, overlook_period
                    )
                    if subject is not None and message is not None:
                        logging.info('Sending onboarding email to ' + user_det[var_names.email] +
                                     '. Subject: ' + subject)
                        mail.AmazonSesDispatcher(subject, message, user_det[var_names.email]).start()

    def pick_onboarding_email(self, registration_date, answers, current_features, recipient_id,
                              overlook_period=None):
        '''
        Selects the onboarding email to send based on the number of days since registration
        and answers provided during onboarding.
        :param registration_date: date the user registered his/her account
        :param answers: (list or None) answers provided by the organization's owner at the time of onboarding
        :param current_features: (list) features that are currently being tried by the organization
        :param recipient_id: (str) encoded unique ID for distinguishing the user
        :param overlook_period: (tuple) -> (start date, end date) period to ignore
        :return: (tuple) -> email subject, email body
        '''
        today_date = self.timestamp.date()
        business_days = helpers.business_day_period(registration_date, today_date, overlook_period)

        mailing_days = [1, 2, 5, 8, 10]
        email_sequence = [x for x in range(1, 6)]

        needs_live_call, needs_customer_support = False, False
        if answers is not None:
            if 'A34' in answers or 'A412' in answers:
                needs_live_call = True

        subject, message = None, None
        if business_days in mailing_days and today_date.weekday() not in [5, 6]:
            expected_email_count = mailing_days.index(business_days)
            if expected_email_count < len(email_sequence):
                email_number = email_sequence[expected_email_count]

                if email_number == 1:
                    if needs_live_call:
                        if constants.call_routing not in current_features:
                            subject, message = informational_content.live_call_routing_content(recipient_id)
                    else:
                        if constants.application_monitoring not in current_features:
                            subject, message = informational_content.configurations_that_must_be_set_up(recipient_id)
                elif email_number == 2:
                    subject, message = informational_content.incident_handling_content(recipient_id)
                elif email_number == 3:
                    subject, message = informational_content.auto_identify_system_impact_content(recipient_id)
                elif email_number == 4:
                    subject, message = informational_content.conditional_routing_content(recipient_id)
                elif email_number == 5:
                    subject, message = informational_content.analytics_content(recipient_id)

        return subject, message


if __name__ == '__main__':
    arg_parser = argparse.ArgumentParser()
    arg_parser.add_argument('--monitor_time', default=None)

    args = arg_parser.parse_args()
    monitor_time = args.monitor_time
    if monitor_time is None:
        monitor_time = times.get_current_timestamp()
    else:
        assert (isinstance(monitor_time, datetime.datetime) or isinstance(monitor_time, str))
        if type(monitor_time) is str:
            monitor_time = datetime.datetime.strptime(monitor_time, constants.timestamp_format)

    db_conn = CONN_POOL.get_db_conn()

    monitor = TrialPeriodMonitor(db_conn, CACHE_CLIENT, monitor_time)
    monitor.notify()
    monitor.monitor()
    monitor.send_onboarding_emails()

    CONN_POOL.put_db_conn(db_conn)
