from pprint import pprint
import pytz
import gocardless_pro as gocardless
import braintree
import logging

from django.conf import settings
payment_providers = settings.PAYMENT_PROVIDERS
logger = logging.getLogger(__name__)
# import paypalrestsdk as paypal

PROVIDER_ID = {'gocardless':1, 'braintree': 2}
PROVIDER_NAME = {1: 'gocardless', 2: 'braintree'}


def select_provider(type):
    if type == "gocardless": return gocardless_provider()
    if type == "braintree": return braintree_provider()
    if type == "paypal": return paypal_provider()

    log.exception('[scaffold] - "No Provider for ' + type)
    assert 0, "No Provider for " + type


class gocardless_provider:
    """
    gocardless test account details 20-00-00, 55779911
    """
    form_remote = True
    client = None

    def __init__(self):
        # gocardless are changing there api, not sure if we can switch yet
        self.client = gocardless.Client(
            access_token=payment_providers['gocardless']['credentials']['access_token'],
            environment=payment_providers['gocardless']['environment'])

    def subscribe_confirm(self, args):
        response = gocardless.client.confirm_resource(args)
        subscription = gocardless.client.subscription(args.get('resource_id'))
        return {
            'amount': subscription.amount,
            'start_date': subscription.created_at,
            'reference': subscription.id,
            'success': response.success
        }

    def fetch_customers(self):
        """Fetch list of customers payments"""
        for customer in self.client.customers.list().records:
            for payment in self.client.payments.list(params={"customer": customer.id}).records:
                yield {
                    'user_reference': customer.id,
                    'email': customer.email,
                    'status': payment.status,
                    'payment_id': payment.links.subscription,
                    'payment_type': 'subscription' if payment.links.subscription else 'payment',
                    'payment_date': payment.created_at,
                    'amount': payment.amount
                }


    def fetch_subscriptions(self):
        # for paying_member in self.client.mandates.list().records:
        for paying_member in self.client.subscriptions.list().records:
            mandate=self.client.mandates.get(paying_member.links.mandate)
            user=self.client.customers.get(mandate.links.customer)

            # gocardless does not have a reference so we use the id instead
            yield {
                'status': paying_member.status,
                'email': user.email,
                'start_date': paying_member.created_at,
                'reference': mandate.id,
                'amount': paying_member.amount}

    def get_redirect_url(self):
        return payment_providers['gocardless']['redirect_url']

    def get_token(self):
        return 'N/A'

    def cancel_subscription(self, reference):
        try:
            subscription = gocardless.client.subscription(reference)
            response = subscription.cancel()
        except  gocardless.exceptions.ClientError:
            return {
                'success': False
            }
        return {
            'amount': subscription.amount,
            'start_date': subscription.created_at,
            'reference': subscription.id,
            'success': response.get('success', False)
        }

    def create_subscription(self, user, session, amount,
                            name, redirect_success, redirect_failure,
                            interval_unit='monthly', interval_length='1'):
        return self.client.redirect_flows.create(params={
            "description": name,
            "session_token": session,
            "success_redirect_url": redirect_success,
            "prefilled_customer": {
                "given_name": user.first_name,
                "family_name": user.last_name,
                "email": user.email
            }
        })


    def confirm_subscription(self, membership, session, provider_response,
                             name, interval_unit='monthly', interval_length='1'):
        r = provider_response.get('redirect_flow_id')

        # response = self.client.redirect_flows.complete(r, params={
        #     "session_token": session
        # })
        response = self.client.redirect_flows.get(r)
        # response = self.client.redirect_flows.get(provider_response.get('redirect_flow_id'))

        # response = gocardless.client.confirm_resource(provider_response)
        # subscription = gocardless.client.subscription(provider_response.get('resource_id'))
        user_id = response.links.customer
        mandate_id = response.links.mandate
        # user = subscription.user()
        user = self.client.customers.get(response.links.customer)
        mandate = self.client.mandates.get(response.links.mandate)
        logging.debug(user)
        logging.debug(mandate)

        #  for some reason go cardless is in pence, so 20.00 needs to be sent as 2000
        #  what genious decided that was a good idea, now looks like i am charging £2000 :p
        #  the return is the same so you need to convert on send and receive
        subscription_response = self.client.subscriptions.create(
            params={
                'amount': str(membership.payment).replace('.', ''),
                'currency': 'GBP',
                'interval_unit': interval_unit,
                'name': name,
                # 'metadata': {'reference': },
                'links': {'mandate': mandate_id}
            })
        return {
            'amount': membership.payment,
            'email': user.email,
            'start_date': subscription_response.created_at,
            'reference': mandate_id,
            'success': subscription_response.api_response.status_code
        }


class braintree_provider:
    form_remote = False

    def __init__(self):
        braintree.Configuration.configure(
            environment=braintree.Environment.Sandbox,
            merchant_id=payment_providers['braintree']['credentials']['merchant_id'],
            public_key=payment_providers['braintree']['credentials']['public_key'],
            private_key=payment_providers['braintree']['credentials']['private_key'])

    def get_token(self):
        return braintree.ClientToken.generate()


    def create_subscription(self, amount, name, redirect_success, redirect_failure, interval_unit='month', interval_length='1'):
        result = braintree.Customer.create({
            "first_name": "test",
            "last_name": "user",
            "payment_method_nonce": nonce_from_the_client
        })

        return braintree.Subscription.create({
            "payment_method_token": "the_token",
            "plan_id": "membership",
            "merchant_account_id": "gbp_sandbox"
            #"price": "20.00"
            #'name': name
        })

    def confirm_subscription(self, args):
        if self.provider == 'gocardless':
            response = gocardless.client.confirm_resource(args)
            subscription = gocardless.client.subscription(args.get('resource_id'))
            return {
                'amount': subscription.amount,
                'start_date': subscription.created_at,
                'reference': subscription.id
            }


    def fetch_subscriptions(self):
        for paying_member in braintree.Subscription.search(braintree.SubscriptionSearch.status == braintree.Subscription.Status.Active):
            user=paying_member.user()
            yield {
                'status': paying_member.status,
                'email': user.email,
                'start_date': paying_member.created_at,
                'reference': paying_member.reference,
                'amount': paying_member.amount}


class payment:
    """
    https://developer.gocardless.com/api-reference/#redirect-flows-create-a-redirect-flow
    paypal reference = https://github.com/paypal/PayPal-Python-SDK
    gocardless reference = https://github.com/paypal/PayPal-Python-SDK
    """
    #~ def __call__(self, **args):
        #~ return self

    def __init__(self, provider='gocardless', style='payment', mode='sandbox'):
        self.provider = provider
        self.environment = int(mode=='production')
        self.provider_id = PROVIDER_ID.get(provider)

        print(payment_providers)
        if provider == 'paypal':
            paypal.configure(**payment_providers[provider]['credentials'])
            return

        gocardless_pro.Client(
            access_token=payment_providers[provider]['credentials']['access_token'],
            environment=payment_providers[provider])
        #~ environment = int('production' = payment_providers[provider]['environment'])
        gocardless.environment = payment_providers[provider]['environment']
        gocardless.set_details(**payment_providers[provider]['credentials'])
        merchant = gocardless.client.merchant()

    def lookup_provider_by_id(self, provider_id):
        return PROVIDER_NAME.get(provider_id, None)

    def make_donation(self, amount, reference, redirect_success, redirect_failure):
        if self.provider == 'paypal':
            payment = paypal.Payment({
                "intent": "sale",
                "payer": {"payment_method": "paypal"},
                "redirect_urls": {
                    "return_url": redirect_success,
                    "cancel_url": redirect_failure},

                "transactions": [{
                    "amount": {
                        "total": amount,
                        "currency": "GBP"},
                    "description": reference}]})

            payment_response = payment.create()
            print('payment create')
            if payment_response:
                print(payment_response)
                for link in payment.links:
                    if link.method == "REDIRECT":
                        redirect_url = str(link.href)
                        print(redirect_url)
                        return str(redirect_url)
            else:
                print("Error while creating payment:")
                print(payment.error)

        if self.provider == 'gocardless':
            return gocardless.client.new_bill_url(
                float(amount),
                name=reference,
                redirect_uri=redirect_success)

        return 'Error something went wrong'

    def fetch_subscriptions(self):
        if self.provider == 'gocardless':
            merchant = gocardless.client.merchant()
            for paying_member in merchant.subscriptions():
                user=paying_member.user()
                print(dir(paying_member)) 
                print(paying_member.next_interval_start) 
                print(paying_member.status) 
                print(dir(paying_member.user())) 
                yield {
                    'email': user.email,
                    'start_date': paying_member.created_at,
                    'reference': paying_member.id,
                    'amount': paying_member.amount}


    def confirm_subscription(self, args):
        if self.provider == 'gocardless':
            response = gocardless.client.confirm_resource(args)
            subscription = gocardless.client.subscription(args.get('resource_id'))
            return {
                'amount': subscription.amount,
                'start_date': subscription.created_at,
                'reference': subscription.id
            }

        if self.provider == 'paypal':
            print('subscribe_confirm')
            payment_token = args.get('token', '')
            billing_agreement_response = paypal.BillingAgreement.execute(payment_token)
            amount = 0
            print(billing_agreement_response)
            print(billing_agreement_response.id)
            for row in billing_agreement_response.plan.payment_definitions:
                amount = row.amount.value

            return {
                'amount': amount,
                'start_date': billing_agreement_response.start_date,
                'reference': billing_agreement_response.id
            }

        return None

    def unsubscribe(self, reference):
        if self.provider == 'gocardless':
            print('unsubscribe gocardless')
            subscription = gocardless.client.subscription(reference)
            print(subscription.cancel())

        if self.provider == 'paypal':
            # this may be wrong
            # ManageRecurringPaymentsProfileStatus 
            print(reference)
            billing_plan = paypal.BillingAgreement.find(reference)
            print(billing_plan)
            print(billing_plan.error)
            #~ billing_plan.replace([{"op": "replace","path": "/","value": {"state":"DELETED"}}])
            print(billing_plan.error)
            #~ invoice = paypal.Invoice.find(reference)
            options = {
                "subject": "Cancelling membership",
                "note": "Canceling invoice",
                "send_to_merchant": True,
                "send_to_payer": True
            }

            if billing_plan.cancel(options):  # return True or False
                print("Invoice[%s] cancel successfully" % (invoice.id))
            else:
                print(billing_plan.error)


    def subscribe(self, amount, name, redirect_success, redirect_failure, interval_unit='month', interval_length='1'):
        if self.provider == 'gocardless':
            return gocardless.client.new_subscription_url(
                amount=float(amount), 
                interval_length=interval_length, 
                interval_unit=interval_unit,
                name=name,
                redirect_uri=redirect_success)

        if self.provider == 'paypal':
            billing_plan = paypal.BillingPlan({
                "name": name,
                "description": "Membership subscription",
                "merchant_preferences": {
                    "auto_bill_amount": "yes",
                    "cancel_url": redirect_failure,
                    "initial_fail_amount_action": "continue",
                    "max_fail_attempts": "1",
                    "return_url": redirect_success,
                    "setup_fee": {
                        "currency": "GBP",
                        "value": amount
                    }
                },
                "payment_definitions": [{
                        "amount": {
                            "currency": "GBP",
                            "value": amount
                        },
                        "cycles": "0",
                        "frequency": interval_unit,
                        "frequency_interval": interval_length,
                        "name": "Regular 1",
                        "type": "REGULAR"
                    }
                ],
                "type": "INFINITE"
            })
            print('create bill')
            
            response = billing_plan.create()
            
            billing_plan = paypal.BillingPlan.find(billing_plan.id)
            
            if billing_plan.activate():
                start_date = datetime.utcnow() + timedelta(minutes=10)
                billing_agreement = paypal.BillingAgreement({
                    "name": billing_plan.name,
                    "description": name,
                    "start_date": start_date.strftime('%Y-%m-%dT%H:%M:%SZ'),
                    "plan": {"id": str(billing_plan.id)},
                    "payer": {"payment_method": "paypal"}
                })
                
                if billing_agreement.create():
                    print('billing agreement id')
                    print(billing_agreement.id)
                    
                    for link in billing_agreement.links:
                        if link.rel == "approval_url":
                            approval_url = link.href
                            return approval_url
                else:
                    print(billing_agreement.error)
                    print('failed')

    def confirm(self, args):
        confirm_details = {}
        confirm_details['successfull'] = False
        print('---------------------')
        print(args)

        from pprint import pprint
        if self.provider == 'paypal':
            print(args.get('paymentId'))
            print(args.get('PayerID'))
            payment = paypal.Payment.find(args.get('paymentId'))
            pprint(payment)
            print(pprint(payment))
            print(payment)
            
            confirm_details['name'] = payment['payer']['payer_info'].first_name + ' ' + payment['payer']['payer_info'].last_name
            confirm_details['user'] = payment['payer']['payer_info'].email
            confirm_details['status'] = payment.state
            confirm_details['amount'] = payment['transactions'][0]['amount'].total
            confirm_details['created'] = payment.create_time
            confirm_details['reference'] = payment.id
            pprint(confirm_details)
            
            
            if payment.execute({"payer_id": args.get('PayerID')}):  # return True or False
                confirm_details['successfull'] = True
                print("Payment[%s] execute successfully" % (args.get('paymentId')))
            else:
                print(payment.error)
            return confirm_details

        if self.provider == 'gocardless':
            bill_id = args.get('resource_id')
            gocardless.client.confirm_resource(args)
            if bill_id:
                bill = gocardless.client.bill(bill_id)
                confirm_details['name'] = bill.name
                confirm_details['user'] = bill.user
                confirm_details['status'] = bill.status
                confirm_details['amount'] = bill.amount
                #~ confirm_details['amount_minus_fees'] = bill.amount_minus_fees
                confirm_details['created'] = bill.created_at
                confirm_details['reference'] = bill_id
                confirm_details['successfull'] = True
                return confirm_details
        return None
