import datetime
import grok
import random
import re
from zope.event import notify
from waeup.ikoba.interfaces import MessageFactory as _
from waeup.ikoba.browser.layout import IkobaEditFormPage, action
from waeup.ikoba.payments.interfaces import (
    IPaymentGatewayService, IPayment, IPayable, IPayer, IPayee,
    STATE_AWAITING_GATEWAY_CONFIRM, STATE_PAID)
from waeup.ikoba.payments.payment import (
    Payment, get_payment, find_payable_from_payable_id, format_amount,
    find_payer_from_payer_id, PaymentProviderServiceBase,
    PaymentWaitingForGatewayEvent, PaymentFinishedEvent)


grok.templatedir('browser_templates')


WARN_FINAL_SUBMIT = _(
    'You can not edit your contract after final submission. '
    'Do you really want to submit?'
)


RE_CC_NUMBER = re.compile('^[0-9]{9,25}$')
RE_CSC = re.compile('^[0-9]{3,4}$')


class DemoCreditcardPaymentService(PaymentProviderServiceBase):
    """A demo payment gateway service.

    This one supports credit card payments.
    """
    grok.implements(IPaymentGatewayService)
    grok.name('demo_creditcard')

    title = _(u'Credit Card (Demo Payments)')

    def create_payment(self, payer, payable,  payee=None):
        """Create a payment.
        """
        if not IPayer.providedBy(payer):
            payer = IPayer(payer)
        if not IPayable.providedBy(payable):
            payable = IPayable(payable)
        if (payee is not None) and (not IPayee.providedBy(payee)):
            payee = IPayee(payee)
        payment = Payment(payer, payable, payee)
        payment.gateway_service = 'demo_creditcard'  # must be grok.name above
        return payment

    def next_step(self, payment_id):
        """Tell where to go next.

        Returns (context, view_name). Both may be none.
        """
        payment = get_payment(payment_id)
        if payment is None:
            return None, None
        if payment.state == STATE_AWAITING_GATEWAY_CONFIRM:
            return payment, 'demo_cc2'
        return payment, 'demo_cc1'


class CreditCardStep1(IkobaEditFormPage):
    grok.context(IPayment)
    grok.name('demo_cc1')
    # XXX: Use own permissions for payments
    grok.require('waeup.Authenticated')
    label = "Enter Credit Card Details"
    grok.template('demo_cc_step1')
    pnav = 4

    def validate_form(self):
        fields = ['first_name', 'last_name', 'cc_number', 'csc', 'exp_date']
        cls = 'form-group'
        if len(self.request.form):
            cls += ' has-success'
        result = dict([(x, cls) for x in fields])
        if not len(self.request.form):
            return True, result
        err = 'form-group has-error'
        if not self.first_name:
            result['first_name'] = err
        if not self.last_name:
            result['last_name'] = err
        if not RE_CC_NUMBER.match(self.cc_number):
            result['cc_number'] = err
        if not RE_CSC.match(self.csc):
            result['csc'] = err
        if err in result.values():
            return False, result
        return True, result

    def update(self, first_name=None, last_name=None, cc_number=None,
               month=None, year=None):
        self.payer = IPayer(find_payer_from_payer_id(self.context.payer_id))
        self.payable = IPayable(find_payable_from_payable_id(
            self.context.payable_id))
        form = self.request.form
        self.first_name = form.get('first_name', self.payer.first_name)
        self.last_name = form.get('last_name', self.payer.last_name)
        self.cc_number = form.get('cc_number', '')
        self.month = int(form.get('exp_month', datetime.datetime.now().month))
        self.year = int(form.get('exp_year', datetime.datetime.now().year))
        self.csc = form.get('csc', '')
        self.amount = format_amount(
            self.context.amount, self.context.currency)
        self.months = ''.join([
            '<option%s>%s</option>' % (
                (x == self.month) and ' selected="selected"' or '',
                x) for x in range(1, 13)])
        self.years = ''.join([
            '<option%s>%s</option>' % (
                (x == self.year + 1) and ' selected="selected"' or '',
                x) for x in range(self.year - 1, self.year + 15)])
        self.ok, self.validations = self.validate_form()

    @action(_('Authorize Payment'), warning=WARN_FINAL_SUBMIT,
            style="primary")
    def authorize(self, **data):
        if not self.ok:
            self.flash(_("Please review (red) entries below!"),
                       type='warning')
            return
        # XXX: payment really started, do lots of logging
        self.context.state = STATE_AWAITING_GATEWAY_CONFIRM
        notify(PaymentWaitingForGatewayEvent(self.context))
        self.redirect(self.url(self.context, 'demo_cc2'))
        return

    @action(_('Cancel'))
    def cancel(self, **data):
        """Go back to the payable (if possible) or site home.
        """
        payable_id = getattr(self.context, 'payable_id', '')
        payed_item = find_payable_from_payable_id(payable_id)
        self.flash(_("Payment cancelled."))
        if payed_item is not None:
            # remove context/payment?
            target = payed_item
        else:
            target = grok.getSite()
        self.redirect(self.url(target))


class CreditCardStep2(IkobaEditFormPage):
    grok.context(IPayment)
    grok.name('demo_cc2')
    # XXX: Use own permissions for payments
    grok.require('waeup.Authenticated')
    label = "&nbsp;"
    grok.template('demo_cc_step2')
    pnav = 4

    def update(self):
        cnt = int(self.request.form.get('cnt', '0'))
        self.cnt = cnt + 1
        threshold = random.choice(range(10))
        self.success = False
        # HERE WOULD WE REALLY ASK FOR VERIFICATION
        if threshold <= cnt:
            self.success = True
            self.flash(_("Your payment was finished successfully."))
        if self.request.form.get('SUBMIT', None):
            self.renew()

    def renew(self):
        if not self.success:
            return
        payable_id = getattr(self.context, 'payable_id', '')
        payed_item = find_payable_from_payable_id(payable_id)
        self.context.state = STATE_PAID
        self.context.payment_date = datetime.datetime.utcnow()
        notify(PaymentFinishedEvent(self.context))
        self.redirect(self.url(payed_item))
