'use strict';

var server = require('server');
var csrfProtection = require('*/cartridge/scripts/middleware/csrf');
var RedsysConstants = require('*/cartridge/scripts/constants/redsysConstants');
var redsysHelper = require('*/cartridge/scripts/redsys/helpers/redsysHelper');

server.extend(module.superModule);

/**
 *  Handle Ajax payment (and billing) form submit
 */
/**
 * CheckoutServices-SubmitPayment : The CheckoutServices-SubmitPayment endpoint will submit the payment information and render the checkout place order page allowing the shopper to confirm and place the order
 * @name Base/CheckoutServices-SubmitPayment
 * @function
 * @memberof CheckoutServices
 * @param {middleware} - server.middleware.https
 * @param {middleware} - csrfProtection.validateAjaxRequest
 * @param {httpparameter} - addressSelector - For Guest shopper: A shipment UUID that contains address that matches the selected address. For returning shopper: ab_<address-name-from-address-book>" of the selected address. For both type of shoppers:  "new" if a brand new address is entered
 * @param {httpparameter} - dwfrm_billing_addressFields_firstName - Input field for the shoppers's first name
 * @param {httpparameter} - dwfrm_billing_addressFields_lastName - Input field for the shoppers's last name
 * @param {httpparameter} - dwfrm_billing_addressFields_address1 - Input field for the shoppers's address 1 - street
 * @param {httpparameter} - dwfrm_billing_addressFields_address2 - Input field for the shoppers's address 2 - street
 * @param {httpparameter} - dwfrm_billing_addressFields_country - Input field for the shoppers's address - country
 * @param {httpparameter} - dwfrm_billing_addressFields_states_stateCode - Input field for the shoppers's address - state code
 * @param {httpparameter} - dwfrm_billing_addressFields_city - Input field for the shoppers's address - city
 * @param {httpparameter} - dwfrm_billing_addressFields_postalCode - Input field for the shoppers's address - postal code
 * @param {httpparameter} - csrf_token - hidden input field CSRF token
 * @param {httpparameter} - localizedNewAddressTitle - label for new address
 * @param {httpparameter} - dwfrm_billing_contactInfoFields_email - Input field for the shopper's email address
 * @param {httpparameter} - dwfrm_billing_contactInfoFields_phone - Input field for the shopper's phone number
 * @param {httpparameter} - dwfrm_billing_paymentMethod - Input field for the shopper's payment method
 * @param {httpparameter} - dwfrm_billing_creditCardFields_cardType - Input field for the shopper's credit card type
 * @param {httpparameter} - dwfrm_billing_creditCardFields_cardNumber - Input field for the shopper's credit card number
 * @param {httpparameter} - dwfrm_billing_creditCardFields_expirationMonth - Input field for the shopper's credit card expiration month
 * @param {httpparameter} - dwfrm_billing_creditCardFields_expirationYear - Input field for the shopper's credit card expiration year
 * @param {httpparameter} - dwfrm_billing_creditCardFields_securityCode - Input field for the shopper's credit card security code
 * @param {category} - sensitive
 * @param {returns} - json
 * @param {serverfunction} - post
 */
server.append(
    'SubmitPayment',
    server.middleware.https,
    csrfProtection.validateAjaxRequest,
    function (req, res, next) {
        var paymentForm = server.forms.getForm('billing');
        var paymentMethodIdValue = paymentForm.paymentMethod.value;

        // Save redsysInsiteForm data in session
        if (paymentMethodIdValue === RedsysConstants.REDSYS_CREDIT_CARD) {
            // eslint-disable-next-line no-undef
            session.privacy.redsys = JSON.stringify({
                creditCardToken: paymentForm.redsysInSiteFields.creditCardToken.value,
                creditCardErrorCode: paymentForm.redsysInSiteFields.creditCardErrorCode.value,
                javaEnabled: paymentForm.redsysInSiteFields.javaEnabled.value,
                javaScriptEnabled: paymentForm.redsysInSiteFields.javaScriptEnabled.value,
                userAgent: paymentForm.redsysInSiteFields.userAgent.value,
                browserLanguage: paymentForm.redsysInSiteFields.browserLanguage.value,
                browserColorDepth: paymentForm.redsysInSiteFields.browserColorDepth.value,
                browserScreenHeight: paymentForm.redsysInSiteFields.browserScreenHeight.value,
                browserScreenWidth: paymentForm.redsysInSiteFields.browserScreenWidth.value,
                browserTZ: paymentForm.redsysInSiteFields.browserTZ.value,
                saveCard: paymentForm.redsysInSiteFields.redsysSaveCard.value,
                storedPaymentUUID: req.form.storedPaymentUUID
            });
        }

        return next();
    });

/**
 * CheckoutServices-PlaceOrder : The CheckoutServices-PlaceOrder endpoint places the order
 * @name Base/CheckoutServices-PlaceOrder
 * @function
 * @memberof CheckoutServices
 * @param {middleware} - server.middleware.https
 * @param {category} - sensitive
 * @param {returns} - json
 * @param {serverfunction} - post
 */
server.prepend('PlaceOrder', server.middleware.https, function (req, res, next) {
    var BasketMgr = require('dw/order/BasketMgr');
    var OrderMgr = require('dw/order/OrderMgr');
    var Resource = require('dw/web/Resource');
    var Transaction = require('dw/system/Transaction');
    var URLUtils = require('dw/web/URLUtils');
    var basketCalculationHelpers = require('*/cartridge/scripts/helpers/basketCalculationHelpers');
    var hooksHelper = require('*/cartridge/scripts/helpers/hooks');
    var COHelpers = require('*/cartridge/scripts/checkout/checkoutHelpers');
    var validationHelpers = require('*/cartridge/scripts/helpers/basketValidationHelpers');
    var redsysOrderHelper = require('*/cartridge/scripts/redsys/helpers/redsysOrderHelper');
    var redsysCheckoutHelper = require('*/cartridge/scripts/checkout/redsysCheckoutHelper');
    var addressHelpers = require('*/cartridge/scripts/helpers/addressHelpers');

    var currentBasket = BasketMgr.getCurrentBasket();

    if (!currentBasket) {
        res.json({
            error: true,
            cartError: true,
            fieldErrors: [],
            serverErrors: [],
            redirectUrl: URLUtils.url('Cart-Show').toString()
        });
        return next();
    }

    var paymentForm = server.forms.getForm('billing');
    var paymentMethodID = paymentForm.paymentMethod.value;

    // If payment method is not from Redsys stops prepend execution
    if (paymentMethodID !== RedsysConstants.REDSYS_CREDIT_CARD && paymentMethodID !== RedsysConstants.REDSYS_BIZUM) {
        return next();
    }

    var validatedProducts = validationHelpers.validateProducts(currentBasket);
    if (validatedProducts.error) {
        res.json({
            error: true,
            cartError: true,
            fieldErrors: [],
            serverErrors: [],
            redirectUrl: URLUtils.url('Cart-Show').toString()
        });
        this.emit('route:Complete', req, res);
        return null;
    }

    if (req.session.privacyCache.get('fraudDetectionStatus')) {
        res.json({
            error: true,
            cartError: true,
            redirectUrl: URLUtils.url('Error-ErrorCode', 'err', '01').toString(),
            errorMessage: Resource.msg('error.technical', 'checkout', null)
        });

        this.emit('route:Complete', req, res);
        return null;
    }

    var validationOrderStatus = hooksHelper('app.validate.order', 'validateOrder', currentBasket, require('*/cartridge/scripts/hooks/validateOrder').validateOrder);
    if (validationOrderStatus.error) {
        res.json({
            error: true,
            errorMessage: validationOrderStatus.message
        });

        this.emit('route:Complete', req, res);
        return null;
    }

    // Check to make sure there is a shipping address
    if (currentBasket.defaultShipment.shippingAddress === null) {
        res.json({
            error: true,
            errorStage: {
                stage: 'shipping',
                step: 'address'
            },
            errorMessage: Resource.msg('error.no.shipping.address', 'checkout', null)
        });

        this.emit('route:Complete', req, res);
        return null;
    }

    // Check to make sure billing address exists
    if (!currentBasket.billingAddress) {
        res.json({
            error: true,
            errorStage: {
                stage: 'payment',
                step: 'billingAddress'
            },
            errorMessage: Resource.msg('error.no.billing.address', 'checkout', null)
        });

        this.emit('route:Complete', req, res);
        return null;
    }

    // Calculate the basket
    Transaction.wrap(function () {
        basketCalculationHelpers.calculateTotals(currentBasket);
    });

    // Re-validates existing payment instruments
    var validPayment = COHelpers.validatePayment(req, currentBasket);
    if (validPayment.error) {
        res.json({
            error: true,
            errorStage: {
                stage: 'payment',
                step: 'paymentInstrument'
            },
            errorMessage: Resource.msg('error.payment.not.valid', 'checkout', null)
        });

        this.emit('route:Complete', req, res);
        return null;
    }

    // Re-calculate the payments.
    var calculatedPaymentTransactionTotal = COHelpers.calculatePaymentTransaction(currentBasket);
    if (calculatedPaymentTransactionTotal.error) {
        res.json({
            error: true,
            errorMessage: Resource.msg('error.technical', 'checkout', null)
        });

        this.emit('route:Complete', req, res);
        return null;
    }

    // Creates a new order.
    var order = null;
    var redsysCreditCardPreferences = redsysHelper.getRedsysCreditCardPreferences();
    if (redsysCreditCardPreferences.integrationMode === RedsysConstants.REDSYS_INTEGRATION_INSITE &&
        paymentMethodID === RedsysConstants.REDSYS_CREDIT_CARD) {
        // eslint-disable-next-line no-undef
        var orderNumberToUse = session.privacy.orderNo;
        // Check if order saved in session to pay not exist in case of error in payment
        if (OrderMgr.getOrder(orderNumberToUse)) {
            res.json({
                error: true,
                errorMessage: Resource.msg('error.technical', 'checkout', null)
            });
            this.emit('route:Complete', req, res);
            return null;
        }
        order = redsysOrderHelper.createOrder(currentBasket, orderNumberToUse);
    } else {
        order = COHelpers.createOrder(currentBasket);
    }

    if (!order) {
        res.json({
            error: true,
            errorMessage: Resource.msg('error.technical', 'checkout', null)
        });

        this.emit('route:Complete', req, res);
        return null;
    }

    // Generate encripted data to send to Redsys
    if (redsysCreditCardPreferences.integrationMode === RedsysConstants.REDSYS_INTEGRATION_REDIRECTION ||
        paymentMethodID === RedsysConstants.REDSYS_BIZUM) {
        var redsysFormData = redsysOrderHelper.generateRedsysFormData(order.orderNo, currentBasket);
        if (!redsysFormData) {
            res.json({
                error: true,
                errorMessage: Resource.msg('error.technical', 'checkout', null)
            });

            this.emit('route:Complete', req, res);
            return null;
        }

        // create custom object with redsysForm values to resend if need it
        redsysOrderHelper.generateNotificationCustomObj(redsysFormData.payload, order);
    }

    // Handles payment authorization
    var handlePaymentResult = redsysCheckoutHelper.handlePayments(order, order.orderNo);

    // Handle custom processing post authorization
    var options = {
        req: req,
        res: res
    };
    var postAuthCustomizations = hooksHelper('app.post.auth', 'postAuthorization', handlePaymentResult, order, options, require('*/cartridge/scripts/hooks/postAuthorizationHandling').postAuthorization);
    if (postAuthCustomizations && Object.prototype.hasOwnProperty.call(postAuthCustomizations, 'error')) {
        res.json(postAuthCustomizations);
        this.emit('route:Complete', req, res);
        return null;
    }

    if (handlePaymentResult.error) {
        res.json({
            error: true,
            errorMessage: Resource.msg('error.technical', 'checkout', null)
        });

        this.emit('route:Complete', req, res);
        return null;
    }

    var fraudDetectionStatus = hooksHelper('app.fraud.detection', 'fraudDetection', currentBasket, require('*/cartridge/scripts/hooks/fraudDetection').fraudDetection);
    if (fraudDetectionStatus.status === 'fail') {
        Transaction.wrap(function () { OrderMgr.failOrder(order, true); });

        // fraud detection failed
        req.session.privacyCache.set('fraudDetectionStatus', true);

        res.json({
            error: true,
            cartError: true,
            redirectUrl: URLUtils.url('Error-ErrorCode', 'err', fraudDetectionStatus.errorCode).toString(),
            errorMessage: Resource.msg('error.technical', 'checkout', null)
        });

        this.emit('route:Complete', req, res);
        return null;
    }

    if (redsysCreditCardPreferences.integrationMode === RedsysConstants.REDSYS_INTEGRATION_INSITE &&
        paymentMethodID === RedsysConstants.REDSYS_CREDIT_CARD && !handlePaymentResult.challenge) {
        // Places the order
        var placeOrderResult = redsysCheckoutHelper.placeOrder(order, fraudDetectionStatus, handlePaymentResult.trataPeticionResponse);
        if (placeOrderResult.error) {
            Transaction.wrap(function () { OrderMgr.failOrder(order, true); });
            res.json({
                error: true,
                errorMessage: Resource.msg('error.technical', 'checkout', null)
            });

            this.emit('route:Complete', req, res);
            return null;
        }

        if (req.currentCustomer.addressBook) {
            // save all used shipping addresses to address book of the logged in customer
            var allAddresses = addressHelpers.gatherShippingAddresses(order);
            allAddresses.forEach(function (address) {
                if (!addressHelpers.checkIfAddressStored(address, req.currentCustomer.addressBook.addresses)) {
                    addressHelpers.saveAddress(address, req.currentCustomer, addressHelpers.generateAddressName(address));
                }
            });
        }

        if (order.getCustomerEmail()) {
            COHelpers.sendConfirmationEmail(order, req.locale.id);
        }

        // Reset usingMultiShip after successful Order placement
        req.session.privacyCache.set('usingMultiShipping', false);
    }

    var continueUrl = null;
    if (redsysCreditCardPreferences.integrationMode === RedsysConstants.REDSYS_INTEGRATION_REDIRECTION ||
        paymentMethodID === RedsysConstants.REDSYS_BIZUM) {
        continueUrl = URLUtils.url('Redsys-Form').toString();
    } else if (handlePaymentResult.challenge) {
        continueUrl = URLUtils.url('Redsys-InSiteAuthentication').toString();
    } else {
        continueUrl = URLUtils.url('Order-Confirm').toString();
    }

    // TODO: Exposing a direct route to an Order, without at least encoding the orderID
    //  is a serious PII violation.  It enables looking up every customers orders, one at a
    //  time.
    res.json({
        error: false,
        orderID: order.orderNo,
        orderToken: order.orderToken,
        continueUrl: continueUrl
    });

    this.emit('route:Complete', req, res);
    return null;
});

module.exports = server.exports();
