## $Id: webcheckoutbrowser.py 16587 2021-08-31 06:28:35Z henrik $
##
## Copyright (C) 2021 Uli Fouquet & Henrik Bettermann
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, write to the Free Software
## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
##
import grok
from datetime import datetime, timedelta
from zope.i18n import translate
from zope.component import getUtility
from zope.security import checkPermission
from xml.dom.minidom import parseString
from waeup.kofa.interfaces import IKofaUtils
from waeup.kofa.utils.helpers import to_timezone
from waeup.kofa.browser.layout import UtilityView, KofaPage, KofaFormPage, action
from waeup.kofa.browser.viewlets import ManageActionButton
from waeup.kofa.payments.interfaces import IPayer
from waeup.kofa.students.interfaces import IStudentsUtils
from waeup.kofa.students.browser import OnlinePaymentDisplayFormPage as OPDPStudent
from waeup.kofa.students.browser import StudentBasePDFFormPage
from waeup.kofa.applicants.browser import OnlinePaymentDisplayFormPage as OPDPApplicant
from waeup.kofa.applicants.browser import ApplicantBaseDisplayFormPage
from kofacustom.nigeria.interswitch.helpers import (
    write_payments_log, confirm_transaction)
from kofacustom.nigeria.payments.interfaces import INigeriaOnlinePayment
from kofacustom.nigeria.students.interfaces import INigeriaStudentOnlinePayment
from kofacustom.nigeria.applicants.interfaces import INigeriaApplicantOnlinePayment
from kofacustom.nigeria.interswitch.tests import (
    MERCHANT_ID)
from kofacustom.nigeria.interfaces import MessageFactory as _

GATEWAY_AMT = 0.0

grok.templatedir('browser_templates')

def webcheckout_module_activated(session, payment):
    if payment.r_company and payment.r_company != 'interswitch':
        return False
    try:
        return getattr(grok.getSite()['configuration'][str(session)],
            'interswitch_webcheckout_enabled', False)
    except KeyError:
        session = datetime.now().year
        try:
            return getattr(grok.getSite()['configuration'][str(session)],
                'interswitch_webcheckout_enabled', False)
        except KeyError:
            return False

# Buttons

class WebcheckoutActionButtonStudent(ManageActionButton):
    grok.context(INigeriaOnlinePayment)
    grok.view(OPDPStudent)
    grok.require('waeup.payStudent')
    grok.order(10)
    icon = 'actionicon_pay.png'
    text = _('Pay via Interswitch WebCheckout')
    target = 'webcheckout'

    @property
    def target_url(self):
        if not webcheckout_module_activated(
            self.context.student.current_session, self.context):
            return ''
        if self.context.p_state == 'paid':
            return ''
        return self.view.url(self.view.context, self.target)

class WebcheckoutActionButtonApplicant(WebcheckoutActionButtonStudent):
    grok.view(OPDPApplicant)
    grok.require('waeup.payApplicant')

    @property
    def target_url(self):
        if not webcheckout_module_activated(
            self.context.__parent__.__parent__.year, self.context):
            return ''
        if self.context.p_state != 'unpaid':
            return ''
        return self.view.url(self.view.context, self.target)

class WebCheckoutConfirmTransactionActionButtonStudent(ManageActionButton):
    grok.order(11)
    grok.context(INigeriaOnlinePayment)
    grok.view(OPDPStudent)
    grok.require('waeup.payStudent')
    icon = 'actionicon_call.png'
    text = _('Confirm WebCheckout Transaction')
    target = 'confirm_transaction'

    @property
    def target_url(self):
        if not webcheckout_module_activated(
            self.context.student.current_session, self.context):
            return ''
        if self.context.amount_auth == 0:
            return ''
        if self.context.p_state in ('paid', 'waived', 'scholarship'):
            return ''
        return self.view.url(self.view.context, self.target)

class WebCheckoutConfirmTransactionActionButtonApplicant(
    WebCheckoutConfirmTransactionActionButtonStudent):
    grok.view(OPDPApplicant)
    grok.require('waeup.payApplicant')

    @property
    def target_url(self):
        if not webcheckout_module_activated(
            self.context.__parent__.__parent__.year, self.context):
            return ''
        if self.context.amount_auth == 0:
            return ''
        if self.context.p_state in ('paid', 'waived', 'scholarship'):
            return ''
        return self.view.url(self.view.context, self.target)

# Forwarding pages

class WebCheckoutPageStudent(KofaPage):
    """ View which sends a POST request to the Interswitch
    WebCheckout payment gateway.
    """
    grok.context(INigeriaOnlinePayment)
    grok.name('webcheckout')
    grok.template('student_goto_webcheckout')
    grok.require('waeup.payStudent')
    label = _('Submit data to Interswitch')
    submit_button = _('Submit')

    action = None
    currency = None
    pay_item_id = None
    merchant_code = None
    gateway_amt = GATEWAY_AMT

    def init_update(self):
        if self.context.p_state == 'paid':
            return _("Payment ticket can't be re-sent to WebCheckout.")
        now = datetime.utcnow()
        if self.context.creation_date.tzinfo is not None:
            # That's bad. Please store timezone-naive datetimes only!
            now = self.context.creation_date.tzinfo.localize(now)
        time_delta = now - self.context.creation_date
        if time_delta.days > 7:
            return _("This payment ticket is too old. Please create a new ticket.")
        if self.context.r_company and self.context.r_company != 'interswitch':
            return _("Payment ticket has been used for another payment gateway.")
        student = self.context.student
        kofa_utils = getUtility(IKofaUtils)
        student_utils = getUtility(IStudentsUtils)
        if student_utils.samePaymentMade(student, self.context.p_category,
            self.context.p_item, self.context.p_session):
            return _("This type of payment has already been made.")
        xmldict = {}
        self.category = self.context.category
        tz = kofa_utils.tzinfo
        self.local_date_time = to_timezone(
            self.context.creation_date, tz).strftime("%Y-%m-%d %H:%M:%S %Z")
        self.site_redirect_url = self.url(self.context, 'confirm_transaction')
        self.student = student
        return

    def update(self):
        if not webcheckout_module_activated(
            self.context.student.current_session, self.context):
            self.flash(_('Forbidden'), type='danger')
            self.redirect(self.url(self.context, '@@index'))
            return
        error = self.init_update()
        if error:
            self.flash(error, type='danger')
            self.redirect(self.url(self.context, '@@index'))
        # Already now it becomes an Interswitch payment. We set the net amount
        # and add the gateway amount.
        if not self.context.r_company:
            self.context.net_amt = self.context.amount_auth
            self.context.amount_auth += self.gateway_amt
            self.context.gateway_amt = self.gateway_amt
            self.context.r_company = u'interswitch'
        self.amount_auth = int(100 * self.context.amount_auth)
        return

class WebCheckoutPageApplicant(KofaPage):
    """ View which sends a POST request to the Interswitch
    WebCheckout payment gateway.
    """
    grok.context(INigeriaApplicantOnlinePayment)
    grok.require('waeup.payApplicant')
    grok.template('applicant_goto_webcheckout')
    grok.name('webcheckout')
    label = _('Submit data to Interswitch')
    submit_button = _('Submit')

    action = None
    currency = None
    pay_item_id = None
    merchant_code = None
    gateway_amt = GATEWAY_AMT
    split_accounts = None

    def init_update(self):
        if self.context.p_state != 'unpaid':
            return _("Payment ticket can't be re-sent to WebCheckout.")
        if self.context.__parent__.__parent__.expired \
            and self.context.__parent__.__parent__.strict_deadline:
            return _("Payment ticket can't be sent to WebCheckout. "
                     "Application period has expired.")
        if self.context.r_company and self.context.r_company != 'interswitch':
            return _("Payment ticket has been used for another payment gateway.")
        tz = getUtility(IKofaUtils).tzinfo
        time_delta = datetime.utcnow() - self.context.creation_date
        if time_delta.days > 7:
            return _("This payment ticket is too old. Please create a new ticket.")
        self.applicant = self.context.__parent__
        self.category = self.context.category
        tz = getUtility(IKofaUtils).tzinfo
        self.local_date_time = to_timezone(
            self.context.creation_date, tz).strftime("%Y-%m-%d %H:%M:%S %Z")
        self.site_redirect_url = self.url(self.context, 'confirm_transaction')
        return

    def update(self):
        if not webcheckout_module_activated(
            self.context.__parent__.__parent__.year, self.context):
            self.flash(_('Forbidden'), type='danger')
            self.redirect(self.url(self.context, '@@index'))
            return
        error = self.init_update()
        if error:
            self.flash(error, type='danger')
            self.redirect(self.url(self.context, '@@index'))
        # Already now it becomes an Interswitch payment. We set the net amount
        # and add the gateway amount.
        if not self.context.r_company:
            self.context.net_amt = self.context.amount_auth
            self.context.amount_auth += self.gateway_amt
            self.context.gateway_amt = self.gateway_amt
            self.context.r_company = u'interswitch'
        self.amount_auth = int(100 * self.context.amount_auth)
        return

# Webservice request views

class WebCheckoutConfirmTransactionApplicant(UtilityView, grok.View):
    """ Request webservice view for the WebCheckout gateway
    """
    grok.context(INigeriaApplicantOnlinePayment)
    grok.name('confirm_transaction')
    grok.require('waeup.payApplicant')

    merchant_code = None
    gateway_host = None
    gateway_url = None
    https = True
    split_accounts = None
    mac = ''


    def update(self):
        if not webcheckout_module_activated(
            self.context.__parent__.__parent__.year, self.context):
            return
        if self.context.p_state == 'paid':
            self.flash(_('This ticket has already been paid.'), type='danger')
            return
        applicant = self.context.__parent__
        success, msg, log = confirm_transaction(
            self.context,
            self.merchant_code,
            self.gateway_host,
            self.gateway_url,
            self.https,
            self.mac)
        applicant.writeLogMessage(self, log)
        if not success:
            self.flash(msg, type='danger')
            return
        write_payments_log(applicant.applicant_id, self.context)
        flashtype, msg, log = self.context.doAfterApplicantPayment()
        if log is not None:
            applicant.writeLogMessage(self, log)
        self.flash(msg, type=flashtype)
        return

    def render(self):
        self.redirect(self.url(self.context.__parent__, 'edit'))
        return

class WebCheckoutConfirmTransactionStudent(UtilityView, grok.View):
    """ Request webservice view for the WebCheckout gateway
    """
    grok.context(INigeriaStudentOnlinePayment)
    grok.name('confirm_transaction')
    grok.require('waeup.payStudent')

    merchant_code = None
    gateway_host = None
    gateway_url = None
    https = True
    mac = ''

    def update(self):
        if not webcheckout_module_activated(
            self.context.student.current_session, self.context):
            return
        if self.context.p_state in ('paid', 'waived', 'scholarship'):
            self.flash(_('This ticket has already been paid.'), type='danger')
            return
        student = self.context.student
        success, msg, log = confirm_transaction(
            self.context,
            self.merchant_code,
            self.gateway_host,
            self.gateway_url,
            self.https,
            self.mac)
        student.writeLogMessage(self, log)
        if not success:
            self.flash(msg, type='danger')
            return
        write_payments_log(student.student_id, self.context)
        flashtype, msg, log = self.context.doAfterStudentPayment()
        if log is not None:
            student.writeLogMessage(self, log)
        self.flash(msg, type=flashtype)
        return

    def render(self):
        self.redirect(self.url(self.context, '@@index'))
        return

