import json

from django.utils.translation import ugettext_lazy as _
from django.urls import reverse

from .base import BackendBase


class StripeBackend(BackendBase):
    backend_id = 'stripe'
    backend_verbose_name = _("Stripe")
    backend_display_name = _("Credit Card")
    backend_has_recurring = True

    def get_plan_id(self, period):
        return 'ccvpn_' + period

    def __init__(self, settings):
        if 'API_KEY' not in settings or 'PUBLIC_KEY' not in settings:
            return

        import stripe
        self.stripe = stripe

        stripe.api_key = settings['API_KEY']
        self.pubkey = settings['PUBLIC_KEY']
        self.header_image = settings.get('HEADER_IMAGE', '')
        self.currency = settings.get('CURRENCY', 'EUR')
        self.name = settings.get('NAME', 'VPN Payment')

        self.backend_enabled = True

    def new_payment(self, payment):
        desc = str(payment.time) + ' for ' + payment.user.username
        form = '''
        <form action="{post}" method="POST">
          <script
            src="https://checkout.stripe.com/checkout.js" class="stripe-button"
            data-key="{pubkey}"
            data-image="{img}"
            data-name="{name}"
            data-currency="{curr}"
            data-description="{desc}"
            data-amount="{amount}"
            data-email="{email}"
            data-locale="auto"
            data-zip-code="true"
            data-alipay="true">
          </script>
        </form>
        '''
        return form.format(
            post=reverse('payments:cb_stripe', args=(payment.id,)),
            pubkey=self.pubkey,
            img=self.header_image,
            email=payment.user.email or '',
            name=self.name,
            desc=desc,
            amount=payment.amount,
            curr=self.currency,
        )

    def new_subscription(self, subscr):
        desc = 'Subscription (' + str(subscr.period) + ') for ' + subscr.user.username
        form = '''
        <form action="{post}" method="POST">
          <script
            src="https://checkout.stripe.com/checkout.js" class="stripe-button"
            data-key="{pubkey}"
            data-image="{img}"
            data-name="{name}"
            data-currency="{curr}"
            data-description="{desc}"
            data-amount="{amount}"
            data-email="{email}"
            data-locale="auto"
            data-zip-code="true"
            data-alipay="true">
          </script>
        </form>
        <noscript><p>Please enable JavaScript to use the payment form.</p></noscript>
        '''
        return form.format(
            post=reverse('payments:cb_stripe_subscr', args=(subscr.id,)),
            pubkey=self.pubkey,
            img=self.header_image,
            email=subscr.user.email or '',
            name=self.name,
            desc=desc,
            amount=subscr.period_amount,
            curr=self.currency,
        )

    def cancel_subscription(self, subscr):
        if subscr.status not in ('new', 'unconfirmed', 'active'):
            return

        try:
            cust = self.stripe.Customer.retrieve(subscr.backend_extid)
        except self.stripe.error.InvalidRequestError:
            return

        try:
            # Delete customer and cancel any active subscription
            cust.delete()
        except self.stripe.error.InvalidRequestError:
            pass

        subscr.status = 'cancelled'
        subscr.save()

    def callback(self, payment, request):
        post_data = request.POST

        token = post_data.get('stripeToken')
        if not token:
            payment.status = 'cancelled'
            payment.status_message = _("No payment information was received.")
            return

        months = int(payment.time.days / 30)
        username = payment.user.username

        try:
            charge = self.stripe.Charge.create(
                amount=payment.amount,
                currency=self.currency,
                card=token,
                description="%d months for %s" % (months, username),
            )
            payment.backend_extid = charge['id']

            if charge['refunded'] or not charge['paid']:
                payment.status = 'rejected'
                payment.status_message = _("The payment has been refunded or rejected.")
                payment.save()
                return

            payment.paid_amount = int(charge['amount'])

            if payment.paid_amount < payment.amount:
                payment.status = 'error'
                payment.status_message = _("The paid amount is under the required amount.")
                payment.save()
                return

            payment.status = 'confirmed'
            payment.status_message = None
            payment.save()
            payment.user.vpnuser.add_paid_time(payment.time)
            payment.user.vpnuser.on_payment_confirmed(payment)
            payment.user.vpnuser.save()

        except self.stripe.error.CardError as e:
            payment.status = 'rejected'
            payment.status_message = e.json_body['error']['message']
            payment.save()

    def callback_subscr(self, subscr, request):
        post_data = request.POST
        token = post_data.get('stripeToken')
        if not token:
            subscr.status = 'cancelled'
            subscr.save()
            return

        try:
            cust = self.stripe.Customer.create(
                source=token,
                plan=self.get_plan_id(subscr.period),
            )
        except self.stripe.error.InvalidRequestError:
            return
        except self.stripe.CardError as e:
            subscr.status = 'error'
            subscr.backend_data['stripe_error'] = e.json_body['error']['message']
            return

        # We don't know much about the new Payment, but we know it
        # succeeded. Wekhooks aren't very reliable, so let's mark it as active
        # anyway.
        subscr.status = 'active'
        subscr.backend_extid = cust['id']
        subscr.save()

    def webhook_payment_succeeded(self, event):
        from payments.models import Subscription, Payment

        invoice = event['data']['object']
        customer_id = invoice['customer']

        # Prevent making duplicate Payments if event is received twice
        pc = Payment.objects.filter(backend_extid=invoice['id']).count()
        if pc > 0:
            return

        subscr = Subscription.objects.get(backend_extid=customer_id)
        payment = subscr.create_payment()
        payment.status = 'confirmed'
        payment.paid_amount = invoice['total']
        payment.backend_extid = invoice['id']
        payment.backend_data = {'event_id': event['id']}
        payment.save()

        payment.user.vpnuser.add_paid_time(payment.time)
        payment.user.vpnuser.on_payment_confirmed(payment)
        payment.user.vpnuser.save()
        payment.save()

        subscr.status = 'active'
        subscr.save()

    def webhook(self, request):
        try:
            event_json = json.loads(request.body.decode('utf-8'))
            event = self.stripe.Event.retrieve(event_json["id"])
        except (ValueError, self.stripe.error.InvalidRequestError):
            return False

        if event['type'] == 'invoice.payment_succeeded':
            self.webhook_payment_succeeded(event)
        return True

    def get_ext_url(self, payment):
        if not payment.backend_extid:
            return None
        return 'https://dashboard.stripe.com/payments/%s' % payment.backend_extid

    def get_subscr_ext_url(self, subscr):
        if not subscr.backend_extid:
            return None
        return 'https://dashboard.stripe.com/customers/%s' % subscr.backend_extid
