import logging
from config import ACCOUNTING_MAIL_RECIPIENT, LOG_LEVEL, REDIS_URL, TIMEZONE
from datetime import datetime, timedelta

from pytz import timezone

import celery
import redis
from charges import amount_to_charge, charge, ChargeException
from npsp import Opportunity
from util import send_email

zone = timezone(TIMEZONE)


log_level = logging.getLevelName(LOG_LEVEL)

root = logging.getLogger()
root.setLevel(log_level)


class Log(object):
    """
    This encapulates sending to the console/stdout and email all in one.

    """

    def __init__(self):
        self.log = list()

    def it(self, string):
        """
        Add something to the log.
        """
        logging.debug(string)
        self.log.append(string)

    def send(self):
        """
        Send the assembled log out as an email.
        """
        body = "\n".join(self.log)
        recipient = ACCOUNTING_MAIL_RECIPIENT
        subject = "Batch run"
        send_email(body=body, recipient=recipient, subject=subject)


class AlreadyExecuting(Exception):
    """
    Here to show when more than one job of the same type is running.
    """

    pass


class Lock(object):
    """
    Claim an exclusive lock. Using Redis.
    """

    def __init__(self, key):
        self.key = key
        self.connection = redis.from_url(REDIS_URL)

    def acquire(self):
        if self.connection.get(self.key):
            raise AlreadyExecuting
        self.connection.setex(name=self.key, value="bar", time=1200)

    def release(self):
        self.connection.delete(self.key)


# TODO stop sending this email and just rely on Sentry and logs?


@celery.task()
def charge_cards():

    lock = Lock(key="charge-cards-lock")
    lock.acquire()

    log = Log()

    log.it("---Starting batch job...")

    three_days_ago = (datetime.now(tz=zone) - timedelta(days=10)).strftime("%Y-%m-%d")
    today = datetime.now(tz=zone).strftime("%Y-%m-%d")

    opportunities = Opportunity.list(begin=three_days_ago, end=today)

    log.it("---Processing charges...")

    log.it(f"Found {len(opportunities)} opportunities available to process.")

    for opportunity in opportunities:
        if not opportunity.stripe_customer:
            continue
        amount = amount_to_charge(opportunity)
        log.it(
            f"---- Charging ${amount} to {opportunity.stripe_customer} ({opportunity.name})"
        )
        try:
            charge(opportunity)
        except ChargeException as e:
            logging.info("Batch charge error")
            e.send_slack_notification()

    log.send()

    lock.release()


if __name__ == "__main__":
    charge_cards()

[((13, 7, 13, 25), 'pytz.timezone', 'timezone', ({(13, 16, 13, 24): 'TIMEZONE'}, {}), '(TIMEZONE)', False, 'from pytz import timezone\n'), ((16, 12, 16, 43), 'logging.getLevelName', 'logging.getLevelName', ({(16, 33, 16, 42): 'LOG_LEVEL'}, {}), '(LOG_LEVEL)', False, 'import logging\n'), ((18, 7, 18, 26), 'logging.getLogger', 'logging.getLogger', ({}, {}), '()', False, 'import logging\n'), ((77, 1, 77, 14), 'celery.task', 'celery.task', ({}, {}), '()', False, 'import celery\n'), ((90, 20, 90, 69), 'npsp.Opportunity.list', 'Opportunity.list', (), '', False, 'from npsp import Opportunity\n'), ((35, 8, 35, 29), 'logging.debug', 'logging.debug', ({(35, 22, 35, 28): 'string'}, {}), '(string)', False, 'import logging\n'), ((45, 8, 45, 67), 'util.send_email', 'send_email', (), '', False, 'from util import send_email\n'), ((63, 26, 63, 51), 'redis.from_url', 'redis.from_url', ({(63, 41, 63, 50): 'REDIS_URL'}, {}), '(REDIS_URL)', False, 'import redis\n'), ((99, 17, 99, 46), 'charges.amount_to_charge', 'amount_to_charge', ({(99, 34, 99, 45): 'opportunity'}, {}), '(opportunity)', False, 'from charges import amount_to_charge, charge, ChargeException\n'), ((88, 12, 88, 33), 'datetime.datetime.now', 'datetime.now', (), '', False, 'from datetime import datetime, timedelta\n'), ((104, 12, 104, 31), 'charges.charge', 'charge', ({(104, 19, 104, 30): 'opportunity'}, {}), '(opportunity)', False, 'from charges import amount_to_charge, charge, ChargeException\n'), ((87, 22, 87, 43), 'datetime.datetime.now', 'datetime.now', (), '', False, 'from datetime import datetime, timedelta\n'), ((87, 46, 87, 64), 'datetime.timedelta', 'timedelta', (), '', False, 'from datetime import datetime, timedelta\n'), ((106, 12, 106, 46), 'logging.info', 'logging.info', ({(106, 25, 106, 45): '"""Batch charge error"""'}, {}), "('Batch charge error')", False, 'import logging\n')]