import string
import random
import requests
import logging

from django.shortcuts import redirect
from django.utils.translation import ugettext_lazy as _
from django.urls import reverse
from django.conf import settings as project_settings

from .base import BackendBase

logger = logging.getLogger(__name__)
prng = random.SystemRandom()


def generate_token(length=16):
    charset = string.digits + string.ascii_letters
    return ''.join([prng.choice(charset) for _ in range(length)])


class CoinGateBackend(BackendBase):
    backend_id = 'coingate'
    backend_verbose_name = _("CoinGate")
    backend_display_name = _("Cryptocurrencies")
    backend_has_recurring = False

    def __init__(self, settings):
        self.api_token = settings.get('api_token')
        if not self.api_token:
            return

        self.currency = settings.get('currency', 'EUR')
        self.title = settings.get('title', 'VPN Payment')

        if settings.get('sandbox'):
            self.sandbox = True
            self.api_base = "https://api-sandbox.coingate.com"
        else:
            self.sandbox = False
            default_base = "https://api.coingate.com"
            self.api_base = settings.get('api_base', default_base)

        self.backend_enabled = True

    def _post(self, endpoint, **kwargs):
        headers = {
            'Authorization': 'Token ' + self.api_token,
        }
        url = self.api_base + endpoint
        response = requests.post(url, headers=headers, **kwargs)
        response.raise_for_status()
        j = response.json()
        return j

    def new_payment(self, payment):
        root_url = project_settings.ROOT_URL
        assert root_url

        token = generate_token()

        order = self._post('/v2/orders', data={
            'order_id': str(payment.id),
            'price_amount': payment.amount / 100,
            'price_currency': self.currency,
            'receive_currency': self.currency,
            'title': self.title,
            'callback_url': root_url + reverse('payments:cb_coingate', args=(payment.id,)),
            'cancel_url': root_url + reverse('payments:cancel', args=(payment.id,)),
            'success_url': root_url + reverse('payments:view', args=(payment.id,)),
            'token': token,
        })

        url = order['payment_url']

        payment.backend_extid = order['id']
        payment.backend_data['coingate_id'] = order['id']
        payment.backend_data['coingate_url'] = url
        payment.backend_data['coingate_token'] = token
        payment.save()

        return redirect(url)

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

        # Verify callback authenticity

        saved_token = payment.backend_data.get('coingate_token')
        if not saved_token:
            logger.debug("payment does not have a coingate_token")
            return False

        token = post_data.get('token')
        if token != saved_token:
            logger.debug("unexpected token (%r != %r)", token, saved_token)
            return False

        order_id = post_data.get('order_id')
        if order_id != str(payment.id):
            logger.debug("unexpected order_id (%r != %r)", order_id, str(payment.id))
            return False

        # Handle event
        status = post_data.get('status')
        if status == 'new' or status == 'pending':
            payment.update_status('new')
        elif status == 'confirming':
            payment.update_status('new', _("Confirming transaction"))
        elif status == 'paid':
            if payment.status in {'new', 'cancelled', 'error'}:
                # we don't have the exact converted amount, but it's not
                # important. settings to the requested amount for consistency
                payment.paid_amount = payment.amount
                payment.confirm()
        elif status == 'invalid' or status == 'expired':
            if payment.status != 'confirmed':
                payment.update_status('cancelled')
        elif status == 'refunded':
            if payment.status == 'confirmed':
                payment.refund()
        else:
            logger.debug("unexpected payment status: %r", status)
            return False

        payment.save()
        return True

    def get_ext_url(self, payment):
        if not payment.backend_extid:
            return None
        if self.sandbox:
            return 'https://sandbox.coingate.com/account/orders/%s' % payment.backend_extid
        else:
            return 'https://coingate.com/account/orders/%s' % payment.backend_extid


