class CustomerFacade(object):
    """
    Acts as a facade to handle Braintree customers.
    """
    def __init__(self):
        self.customer_dao = CustomerDAO()
        self.customer_integration = BraintreeCustomer()

    def create_customer(self,
                        nonce,
                        first_name,
                        last_name,
                        user_id,
                        *,
                        make_default=False,
                        skip_commit=False):
        """
        Handles creating a customer account with a unique identifier and a
        payment method.

        :param str nonce: A nonce identifying a card -- created by BT
        hosted fields on the FE.
        :param str first_name: The customer's first name.
        :param str last_name: The customer's last name.
        :param str user_id: The user's unique UUID.
        :param bool make_default: If a user specifies to make this their
        default payment method, we want to assign that value.
        :param bool skip_commit: Should we skip committing? Facades on facades
        :rtype: CustomerTable
        :return: The newly created customer.
        """
        customer_token = str(uuid.uuid4())

        new_customer = self.customer_integration.create_customer(
            nonce,
            customer_token,
            first_name,
            last_name,
            user_id,
            make_default=make_default)

        return self.customer_dao.create_customer(
            "bt_customer_id",
            "cc_token",
            first_name,
            last_name,
            user_id,
            skip_commit=skip_commit,
        )

    def delete_customer(self):
        pass
    def setUpClass(cls):
        with app.app_context():
            db.drop_all()
            db.create_all()

            cls.test_dao = CustomerDAO()
 def __init__(self):
     self.customer_facade = CustomerFacade()
     self.customer_dao = CustomerDAO()
     self.subscription_integration = BraintreeSubscription()
     self.user_dao = UserDAO()
     self.subscription_dao = SubscriptionDAO()
class SubscriptionFacade(object):
    """
    Acts as a facade to handle Braintree submerchants.
    """
    def __init__(self):
        self.customer_facade = CustomerFacade()
        self.customer_dao = CustomerDAO()
        self.subscription_integration = BraintreeSubscription()
        self.user_dao = UserDAO()
        self.subscription_dao = SubscriptionDAO()

    def create_new_subscription_with_customer(
            self, user_id, nonce, first_name, last_name,
            *, skip_commit=True, plan_id='Basic0x01'):
        """
        Handles creating a new customer with a new subscription.

        :param str user_id: The user to create the customer and subscription
        for.
        :param str nonce: A nonce identifying a card -- created by BT
        hosted fields on the FE.
        :param str first_name: The user's first name.
        :param str last_name: The user's last name.
        :param bool skip_commit: Should we skip committing? Facades on facades
        :param str plan_id: The plan ID. Don't touch!
        :rtype: SubscriptionTable
        :return: The newly created subscription.
        """
        found_customer = self._customer_exists(user_id)
        if found_customer:
            return self.create_new_subscription(
                user_id,
                skip_commit=skip_commit,
                plan_id=plan_id,
            )

        try:
            # type: CustomerTable
            new_customer = self.customer_facade.create_customer(
                nonce,
                first_name,
                last_name,
                user_id,
                skip_commit=skip_commit
            )

            sub_id = uuid.uuid4()

            new_subscription = self.subscription_integration.start_subscription(
                sub_id,
                new_customer.credit_card_token,
                plan_id,
            )

            self.subscription_dao.create_subscription(
                sub_id,
                user_id,
                skip_commit=skip_commit,
            )

            db.session.commit()

            return new_subscription
        except Exception as e:
            logging.error(
                'Failed to create new customer for user {0} with '
                'exception {1}'.format(
                    user_id,
                    e,
                )
            )
            db.session.rollback()
            raise FacadeException(
                'Failed to start subscription.'
            )

    def cancel_subscription(self, user_id, *, skip_commit=False):
        """
        Handles cancelling the currently running subscription for the user

        :param str user_id: The user to cancel the subscription for.
        :param bool skip_commit: Should we skip committing? Facades on facades
        :rtype: CustomerTable
        :return: The newly created customer.
        """
        return self.subscription_dao.cancel_subscription(
            user_id,
        )

    def create_new_subscription(self, user_id, *,
                                skip_commit=False, plan_id='Basic0x01'):
        """
        Handles creating a new subscription for an existing customer account.

        :param str user_id: The user to create the subscription for.
        :param bool skip_commit: Should we skip committing? Facades on facades
        :param str plan_id: The plan ID. Don't touch!
        :rtype: SubscriptionTable
        :return: The newly created subscription.
        """
        try:
            found_customer = self.customer_dao.get_customer_by_user_id(user_id)

            unique_id = uuid.uuid4()

            new_subscription = self.subscription_integration.start_subscription(
                unique_id,
                found_customer.credit_card_token,
                plan_id,
            )

            self.subscription_dao.create_subscription(
                unique_id,
                user_id,
                skip_commit=skip_commit,
            )

            db.session.commit()

            return new_subscription
        except Exception as e:
            logging.error(
                'Failed to create new subscription for user {0} with '
                'exception {1} for pre-existing customer.'.format(
                    user_id,
                    e,
                )
            )
            db.session.rollback()
            raise FacadeException(
                'Failed to start subscription.'
            )

    def _customer_exists(self, user_id):
        try:
            return self.customer_dao.get_customer_by_user_id(user_id)
        except DAOException as e:
            return False
 def __init__(self):
     self.customer_dao = CustomerDAO()
     self.customer_integration = BraintreeCustomer()
 def __init__(self):
     self.customer_dao = CustomerDAO()
class SubscriptionDAO(BaseDAO):
    """
    Handles mirroring braintree customer operations in our database.
    """
    def __init__(self):
        self.customer_dao = CustomerDAO()

    def get_subscription_by_user_id(self, user_id):
        """
        Handles retrieving a subscription for the user.

        :param str user_id: The customer's UUID.
        :rtype: SubscriptionTable
        :return: The requested subscription.
        """
        found_subscription = db.session.query(
            Subscription,
        ).filter(
            Subscription.user_id == user_id,
        ).first()

        if found_subscription is None:
            logging.error(
                'Failed to retrieve subscription by User ID {0}'.format(
                    user_id,
                )
            )
            raise DAOException('Failed to retrieve subscription info.')

        return found_subscription

    def get_subscription_by_public_id(self, subscription_public_id):
        """
        Handles retrieving a subscription by it's public ID.

        :param str subscription_public_id: The public ID for the customer
        :rtype: SubscriptionTable
        :return: The requested subscription.
        """
        found_subscription = db.session.query(
            Subscription
        ).filter_by(
            public_id=subscription_public_id
        ).first()

        if found_subscription is None:
            logging.error(
                'Failed to retrieve subscription by ID {0}'.format(
                    subscription_public_id,
                )
            )
            raise DAOException('Failed to retrieve selected subscription.')

        return found_subscription

    def create_subscription(self, bt_sub_id, user_id, *, plan_id='Basic0x01',
                            skip_commit=False):
        """
        Handles creating a subscription record in the db.


        :param str bt_sub_id: The unique identifier generated by braintree
        when subscriptions are created.
        :param str user_id: The customer's UUID.
        :param str plan_id: The plan we want to subscribe to.
        :param bool skip_commit: Handles skipping the commit to create a pseudo
        transaction.
        :rtype: SubscriptionTable
        :return: The newly created subscription
        """
        new_subscription = Subscription(
            self.customer_dao.get_customer_by_user_id(user_id).public_id,
            user_id,
            bt_sub_id,
            plan_id=plan_id,
        )

        try:
            exec_and_commit(
                db.session.add,
                new_subscription,
                skip_commit=skip_commit,
            )
        except Exception as e:
            logging.critical(
                'Failed to create a new subscription in our database: {0} with'
                'exception of: {1}'.format(
                    new_subscription,
                    e,
                )
            )
            raise DAOException(
                'Failed to create new subscription.'
            )

        return new_subscription

    def cancel_subscription(self, user_id):
        """
        Handles cancelling a subscription for the user.

        :param str user_id: The customer whom we want to cancel the
        subscription for.
        :rtype: SubscriptionTable
        :return: The recently cancelled subscription
        """
        found_subscription = self.get_subscription_by_user_id(user_id)

        if found_subscription is None:
            raise DAOException(
                'Subscription not found.',
                status_code=HTTPStatus.NOT_FOUND
            )

        db.session.query(
            Subscription
        ).filter_by(
            public_id=found_subscription.public_id,
        ).update({
            'is_deleted': True,
            'date_ended': datetime.utcnow(),
        })

        try:
            db.session.commit()
        except sqlalchemy.exc.IntegrityError as e:
            logging.error(
                'Failed to remove subscription for user {0} with '
                'exc: {1}'.format(
                    user_id,
                    e,
                )
            )
            raise DAOException('Failed to cancel subscription.')

        return found_subscription