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, 'pytz.timezone', 'timezone', False, 'from pytz import timezone\n'), (16, 'logging.getLevelName', 'logging.getLevelName', False, 'import logging\n'), (18, 'logging.getLogger', 'logging.getLogger', False, 'import logging\n'), (77, 'celery.task', 'celery.task', False, 'import celery\n'), (90, 'npsp.Opportunity.list', 'Opportunity.list', False, 'from npsp import Opportunity\n'), (35, 'logging.debug', 'logging.debug', False, 'import logging\n'), (45, 'util.send_email', 'send_email', False, 'from util import send_email\n'), (63, 'redis.from_url', 'redis.from_url', False, 'import redis\n'), (99, 'charges.amount_to_charge', 'amount_to_charge', False, 'from charges import amount_to_charge, charge, ChargeException\n'), (88, 'datetime.datetime.now', 'datetime.now', False, 'from datetime import datetime, timedelta\n'), (104, 'charges.charge', 'charge', False, 'from charges import amount_to_charge, charge, ChargeException\n'), (87, 'datetime.datetime.now', 'datetime.now', False, 'from datetime import datetime, timedelta\n'), (87, 'datetime.timedelta', 'timedelta', False, 'from datetime import datetime, timedelta\n'), (106, 'logging.info', 'logging.info', False, 'import logging\n')]