'use strict';

var server = require('server');
var redsysConstants = require('*/cartridge/scripts/constants/redsysConstants');
var redsysHelper = require('*/cartridge/scripts/redsys/helpers/redsysHelper');
var Logger = require('dw/system/Logger');
var logger = Logger.getLogger('Redsys', 'redsys');

/**
 * Redsys-Form : This endpoint is invoked to send order information to Redsys Virtual TPV
 * @param {middleware} server.middleware.https - server.middleware.https
 * @param {httpparameter} orderID - Order Id.
 * @param {httpparameter} orderToken - Order Token.
 * @param {serverfunction} post
 * @param {renders} - isml
 */
server.use(
    'Form',
    server.middleware.https,
    function (req, res, next) {
        var CustomObjectMgr = require('dw/object/CustomObjectMgr');
        var OrderMgr = require('dw/order/OrderMgr');
        var Resource = require('dw/web/Resource');

        var compatibilityModeEnabled = redsysHelper.redsysSFRA5CompatibilityModeEnabled();
        var orderID = null;
        var orderToken = null;

        if (compatibilityModeEnabled) {
            orderID = req.querystring.ID;
            orderToken = req.querystring.token ? req.querystring.token : null;
        } else {
            orderID = req.form.orderID;
            orderToken = req.form.orderToken;
        }

        if (!orderToken || !orderID) {
            res.render('/error', {
                message: Resource.msg('error.confirmation.error', 'confirmation', null)
            });

            next();
        }

        var redirectionUrl = redsysHelper.getRedirectionUrl();
        var order = OrderMgr.getOrder(orderID, orderToken);
        var redsysPaymentForm = CustomObjectMgr.getCustomObject('RedsysNotifications', order.orderNo);

        if (!redsysPaymentForm) {
            res.render('/error', {
                message: Resource.msg('error.confirmation.error', 'confirmation', null)
            });

            return next();
        }

        var redsysFormData = {
            dsSignatureVersion: redsysConstants.RESDSYS_DS_SIGNATURE_VERSION,
            dsSignature: redsysPaymentForm.custom.ds_signature,
            dsMerchantParameters: redsysPaymentForm.custom.ds_merchant_parameters,
            redirectionUrl: redirectionUrl
        };

        res.render('checkout/billing/paymentOptions/redsys/redirectionForm', {
            redsysFormData: redsysFormData
        });

        return next();
    });

/**
 * Redsys-OK : This endpoint is invoked to receive Redsys response after payment process with result success
 * @param {middleware} server.middleware.https - server.middleware.https
 * @param {querystringparameter} orderID - Order ID
 * @param {serverfunction} - get
 * @param {renders} - isml
 */
server.get(
    'OK',
    server.middleware.https,
    function (req, res, next) {
        var redsysPaymentCardHelper = require('*/cartridge/scripts/redsys/helpers/redsysPaymentCardHelper');
        var CustomObjectMgr = require('dw/object/CustomObjectMgr');
        var OrderMgr = require('dw/order/OrderMgr');

        var orderID = req.querystring.orderID;
        try {
            var order = OrderMgr.getOrder(orderID);
        } catch (error) {
            logger.error('Redsys-OK: order {0} not found', orderID);            
        }

        if (!order) {
            res.setStatusCode(500);
            logger.error('Redsys-OK: order {0} not found', orderID);
            return next();
        }

        var redsysNotify = CustomObjectMgr.getCustomObject('RedsysNotifications', orderID);
        // eslint-disable-next-line no-undef
        if (redsysNotify && session.customer.authenticated) {
            var paymentInstruments = order.getPaymentInstruments();
            // Creates a new payment instrument in customer wallet only if save credit card checkbox it's selected
            try {
                redsysPaymentCardHelper.savePaymentInformation(req, paymentInstruments[0], JSON.parse(redsysNotify.custom.ds_merchant_parameters));
            } catch (err) {
                logger.error('Error saving payment card in wallet to order: {0}', orderID);
            }
        }

        res.render('checkout/redsys/redsysConfirmOrder', {
            orderID: order.orderNo,
            orderToken: order.orderToken
        });

        return next();
    });

/**
 * Redsys-KO : This endpoint is invoked to receive Redsys response after payment process with result error
 * @param {middleware} server.middleware.https - server.middleware.https
 * @param {querystringparameter} orderID - Order ID
 * @param {serverfunction} - get
* @return {dw.web.Url} THE url to redirect to in case of error with payment.
 */
server.get('KO',
    server.middleware.https,
    function (req, res, next) {
        var OrderMgr = require('dw/order/OrderMgr');
        var Transaction = require('dw/system/Transaction');
        var URLUtils = require('dw/web/URLUtils');
        var StringUtils = require('dw/util/StringUtils');
        var Resource = require('dw/web/Resource');

        var orderID = req.querystring.orderID;
        try {
            var order = OrderMgr.getOrder(orderID);
        } catch (error) {
            logger.error('Redsys-OK: order {0} not found', orderID);            
        }        
        var queryString = req.querystring;
        var errorMessage = Resource.msg('error.technical', 'checkout', null);

        if (queryString.Ds_MerchantParameters) {
            var redsysResponse = JSON.parse(StringUtils.decodeBase64(queryString.Ds_MerchantParameters));
            errorMessage = redsysResponse.Ds_Response;
        }

        if (!order) {
            logger.error('Redsys-KO: Order {0} not found', orderID);
            res.redirect(URLUtils.url('Checkout-Begin', 'stage', 'payment', 'redsysError', errorMessage));
        } else {
            try {
                Transaction.wrap(function () {
                    OrderMgr.failOrder(order, true);
                });
            } catch (err) {
                logger.error('Redsys-KO: Order {0} not found or was an error updating order status: {1}', orderID, err);
            }
        }

        res.redirect(URLUtils.url('Checkout-Begin', 'stage', 'payment', 'redsysError', errorMessage));

        next();
    });

/**
 * Redsys-Notify : This endpoint is invoked by Redsys to update order information
 * @param {middleware} server.middleware.https - server.middleware.https
 * @param {httpparameter} parameters - Redsys response params.
 * @param {serverfunction} - post
 * @param {returns} - json
 */
server.post('Notify',
    server.middleware.https,
    function (req, res, next) {
        var CustomObjectMgr = require('dw/object/CustomObjectMgr');
        var Transaction = require('dw/system/Transaction');
        var StringUtils = require('dw/util/StringUtils');
        var Status = require('dw/system/Status');
        var Order = require('dw/order/Order');
        var OrderMgr = require('dw/order/OrderMgr');
        var COHelpers = require('*/cartridge/scripts/checkout/checkoutHelpers');

        var parameters = JSON.parse(StringUtils.decodeBase64(req.form.Ds_MerchantParameters));
        var regexpbar = /\//g;
        var regexpplus = /\+/g;

        var redsysNotify = CustomObjectMgr.getCustomObject('RedsysNotifications', parameters.Ds_Order);
        if (redsysNotify) {
            var order = OrderMgr.getOrder(redsysNotify.custom.redsysNotifyId, redsysNotify.custom.orderToken);
            var paymentInstruments = order.getPaymentInstruments();
            var paymentMethod = paymentInstruments[0].paymentMethod;

            var ourSignature = redsysHelper.makeParameters(null, paymentMethod, parameters, parameters.Ds_Order).payload.Ds_Signature.replace(regexpbar, '_').replace(regexpplus, '-');
            var dsResponse = parameters.Ds_Response;
            var dsSignature = req.form.Ds_Signature;

            // Check that the redsys data is correct and the signature is the same.
            if (ourSignature === dsSignature && dsResponse > -1 && dsResponse < 100) {
                try {
                    Transaction.begin();
                    var placeOrderStatus = OrderMgr.placeOrder(order);
                    if (placeOrderStatus === Status.ERROR) throw new Error();
                    order.setConfirmationStatus(Order.CONFIRMATION_STATUS_CONFIRMED);
                    order.setExportStatus(Order.EXPORT_STATUS_READY);
                    order.setPaymentStatus(Order.PAYMENT_STATUS_PAID);
                    redsysNotify.custom.ds_merchant_parameters = JSON.stringify(parameters);
                    Transaction.commit();

                    Transaction.wrap(function () {
                        if (paymentMethod === redsysConstants.REDSYS_CREDIT_CARD) {
                            paymentInstruments[0].custom.redsysCreditCardPaymentID = parameters.Ds_Order;
                            paymentInstruments[0].custom.redsysCreditCardPaymentData = JSON.stringify(parameters);
                        }

                        if (paymentMethod === redsysConstants.REDSYS_BIZUM) {
                            paymentInstruments[0].custom.redsysBizumPaymentID = parameters.Ds_Order;
                            paymentInstruments[0].custom.redsysBizumPaymentData = JSON.stringify(parameters);
                        }
                    });
                } catch (err) {
                    logger.error('Error updating order: {0} - {1}', parameters.Ds_Order, err);
                    res.setStatusCode(500);
                    res.json({
                        error: true,
                        msg: 'Error updating order'
                    });
                }
            }
            // Sends a confirmation to the current user when Redsys approved payment and the order status is not failed
            if (order.status.value !== Order.ORDER_STATUS_FAILED) {
                COHelpers.sendConfirmationEmail(order, req.locale.id);
            }
        } else {
            logger.error('RedsysNotification not found with order number {0}. Order was not updated to status paid', parameters.Ds_Order);
            res.setStatusCode(500);
            res.json({
                error: true,
                msg: 'Error updating order'
            });
        }

        res.setStatusCode(200);
        res.json({
            error: false,
            msg: 'Order updated'
        });

        next();
    });

/**
 * Redsys-InSiteData : This endpoint is invoked when user select credit card with inSite mode and returns the info to send to Redsys
 * @param {middleware} server.middleware.https - server.middleware.https
 * @param {httpparameter} parameters - Redsys response params.
 * @param {serverfunction} - post
 * @param {returns} - json
 */
server.get(
    'InSiteData',
    server.middleware.https,
    function (req, res, next) {
        var OrderMgr = require('dw/order/OrderMgr');

        var redsysCreditCardPreferences = redsysHelper.getRedsysCreditCardPreferences();
        var getOrderNumber = OrderMgr.createOrderSequenceNo();
        // eslint-disable-next-line no-undef
        session.privacy.orderNo = getOrderNumber;

        var inSiteData = {
            terminal: redsysCreditCardPreferences.merchantTerminal,
            fuc: redsysCreditCardPreferences.merchantCode,
            orderNo: getOrderNumber,
            // eslint-disable-next-line no-undef
            language: request.locale ? request.locale.split('_')[1] : null
        };

        res.json({
            success: true,
            inSiteData: inSiteData
        });

        next();
    });

/**
 * Redsys-InSiteNotify : This endpoint is invoked by Redsys to send the autorizacion response
 * @param {middleware} server.middleware.https - server.middleware.https
 * @param {httpparameter} parameters - Redsys response params.
 * @param {serverfunction} - post
 */
server.post('InSiteNotify',
    server.middleware.https,
    function (req, res, next) {
        var COHelpers = require('*/cartridge/scripts/checkout/checkoutHelpers');
        var redsysApi = require('*/cartridge/scripts/redsys/redsysApi');
        var redsysPaymentCardHelper = require('*/cartridge/scripts/redsys/helpers/redsysPaymentCardHelper');
        var status = require('dw/system/Status');
        var transaction = require('dw/system/Transaction');
        var Order = require('dw/order/Order');
        var OrderMgr = require('dw/order/OrderMgr');
        var URLUtils = require('dw/web/URLUtils');

        var queryString = res.viewData.queryString;
        var orderNo = queryString.split('=')[1];
        var regexpbar = /\//g;
        var regexpplus = /\+/g;
        var formData = req.form;

        // Authentication step
        var authentication = redsysApi.autenticacion(orderNo, formData);
        var order = OrderMgr.getOrder(orderNo);
        var dsResponse = authentication.response.Ds_Response;

        // Return to payment step if autentication fail
        if (authentication.status === redsysConstants.REDSYS_RESPONSE_FAIL) {
            transaction.wrap(() => {
                OrderMgr.failOrder(order, true);
            });

            res.redirect(URLUtils.url('Checkout-Begin', 'stage', 'payment', 'redsysError', dsResponse));
            return next();
        }

        var paymentInstruments = order.getPaymentInstruments();
        var authenticationSignature = authentication.signature;
        var ourSignature = redsysHelper.makeParameters(
            null,
            'REDSYS_CREDIT_CARD',
            authentication.response,
            orderNo)
            .payload.Ds_Signature.replace(regexpbar, '_')
            .replace(regexpplus, '-'
        );

        if (ourSignature !== authenticationSignature || (dsResponse < -1 || dsResponse > 100)) {
            logger.info('Error in order {0}: {1}', orderNo, dsResponse);
            transaction.wrap(() => {
                OrderMgr.failOrder(order, true);
            });
            res.redirect(URLUtils.url('Checkout-Begin', 'stage', 'payment', 'redsysError', dsResponse));

            return next();
        }

         // eslint-disable-next-line no-undef
        if (session.customer.authenticated) {
            // Creates a new payment instrument in customer wallet only if save credit card checkbox it's selected
            try {
                redsysPaymentCardHelper.savePaymentInformation(req, paymentInstruments[0], authentication.response);
            } catch (err) {
                logger.error('Error saving payment card in wallet to order: {0}', orderNo);
            }
        }

        try {
            transaction.begin();
            var placeOrderStatus = OrderMgr.placeOrder(order);
            if (placeOrderStatus === status.ERROR) throw new Error();
            order.setConfirmationStatus(Order.CONFIRMATION_STATUS_CONFIRMED);
            order.setExportStatus(Order.EXPORT_STATUS_READY);
            order.setPaymentStatus(Order.PAYMENT_STATUS_PAID);
            transaction.commit();

            transaction.wrap(function () {
                paymentInstruments[0].custom.redsysCreditCardPaymentID = orderNo;
                paymentInstruments[0].custom.redsysCreditCardPaymentData = JSON.stringify(authentication.response);
            });
        } catch (err) {
            logger.error('Error updating order');
            transaction.wrap(() => {
                OrderMgr.failOrder(order, true);
            });
            res.setStatusCode(500);
            res.json({
                error: true,
                msg: 'Error updating order'
            });

            return next();
        }

        // Sends a confirmation to the current user when Redsys approved payment and the order status is not failed
        if (order.status.value !== order.ORDER_STATUS_FAILED) {
            COHelpers.sendConfirmationEmail(order, req.locale.id);
        }

        res.render('checkout/redsys/redsysConfirmOrder', {
            orderID: order.orderNo,
            orderToken: order.orderToken
        });

        return next();
    });

/**
 * Redsys-InSiteAuthentication : This endpoint is invoked by Redsys to send the authentication response
 * @param {middleware} server.middleware.https - server.middleware.https
 * @param {httpparameter} parameters - Redsys response params.
 * @param {serverfunction} - post
 * @param {returns} - json
 */
server.use('InSiteAuthentication',
    server.middleware.https,
    function (req, res, next) {
        var customObjectMgr = require('dw/object/CustomObjectMgr');
        var Resource = require('dw/web/Resource');
        var URLUtils = require('dw/web/URLUtils');

        var compatibilityModeEnabled = redsysHelper.redsysSFRA5CompatibilityModeEnabled();
        var orderNo = null;

        if (compatibilityModeEnabled) {
            orderNo = req.querystring.ID;
        } else {
            orderNo = req.form.orderID;
        }

        if (!orderNo) {
            res.render('/error', {
                message: Resource.msg('error.confirmation.error', 'confirmation', null)
            });

            return next();
        }

        var redsysCustomObj = customObjectMgr.getCustomObject('RedsysNotifications', orderNo);
        if (!redsysCustomObj) {
            res.redirect(URLUtils.url('Checkout-Begin', 'stage', 'payment'));
            return next();
        }

        var redsysFormData = {
            protocolVersion: redsysCustomObj.custom.protocolVersion,
            creq: redsysCustomObj.custom.creq,
            acsURL: redsysCustomObj.custom.acsURL,
            PaReq: redsysCustomObj.custom.PaReq,
            MD: redsysCustomObj.custom.MD,
            TermUrl: redsysConstants.REDSYS_INSITE_NOTIFICATION_URL + '?order=' + orderNo
        };

        res.render('checkout/billing/paymentOptions/redsys/creditCard/authenticationForm', {
            redsysFormData: redsysFormData
        });

        return next();
    });

module.exports = server.exports();
