text: 'use strict';
  
  /**
   * Controller that displays credit card and other payment information and
   * lets the user change it.
   *
   * @module controllers/PaymentInstruments
   */
  
  /* API includes */
  var PaymentMgr = require('dw/order/PaymentMgr');
  var PaymentStatusCodes = require('dw/order/PaymentStatusCodes');
  var Status = require('dw/system/Status');
  var Transaction = require('dw/system/Transaction');
  var URLUtils = require('dw/web/URLUtils');
  
  /* Script Modules */
  var app = require('~/cartridge/scripts/app');
  var guard = require('~/cartridge/scripts/guard');
  
  /**
   * Displays a list of customer payment instruments.
   *
   * Gets customer payment instrument information. Clears the paymentinstruments form and adds the customer
   * payment information to it. Updates the page metadata.
   * Renders a list of the saved credit card payment instruments of the current
   * customer (account/payment/paymentinstrumentlist template).
   */
  function list() {
      var sitePrefs = dw.system.Site.getCurrent().getPreferences(),
          wallet    = customer.getProfile().getWallet();
  
      if (sitePrefs.custom["hipayEnabled"] && sitePrefs.custom["hipayEnableOneClick"]) {
          var paymentInstruments = wallet.getPaymentInstruments('HIPAY_CREDIT_CARD');
      } else {
          var paymentInstruments = wallet.getPaymentInstruments(dw.order.PaymentInstrument.METHOD_CREDIT_CARD);
      }
  
      var pageMeta = require('~/cartridge/scripts/meta');
      var paymentForm = app.getForm('paymentinstruments');
  
      paymentForm.clear();
      paymentForm.get('creditcards.storedcards').copyFrom(paymentInstruments);
  
      pageMeta.update(dw.content.ContentMgr.getContent('myaccount-paymentsettings'));
  
      app.getView({
          PaymentInstruments: paymentInstruments
      }).render('account/payment/paymentinstrumentlist');
  }
  
  
  /**
   * Adds a new credit card payment instrument to the saved payment instruments of the current customer.
   * Sets the ContinueURL to PaymentInstruments-PaymentForm and renders the payment instrument details page
   * (account/payment/paymentinstrumentdetails template).
   * __Note:__this function is called by the {@link module:controllers/PaymentInstruments~handlePaymentForm|handlePaymentForm} function.
   * @param {boolean} clearForm true or missing clears the form before displaying the page, false skips it
   */
  function add(clearForm) {
      var paymentForm = app.getForm('paymentinstruments');
  
      if (clearForm !== false) {
          paymentForm.clear();
      }
      paymentForm.get('creditcards.newcreditcard.type').setOptions(dw.order.PaymentMgr
          .getPaymentMethod(dw.order.PaymentInstrument.METHOD_CREDIT_CARD).activePaymentCards.iterator());
  
      app.getView({
          ContinueURL: URLUtils.https('PaymentInstruments-PaymentForm')
      }).render('account/payment/paymentinstrumentdetails');
  }
  
  /**
   * Form handler for the paymentinstruments form. Handles the following actions:
   * - __create__ - calls the {@link module:controllers/PaymentInstruments~create|create} function to create a payment instrument
   * and redirects to {@link module:controllers/PaymentInstruments~list|list}. If the
   * creation fails, calls the {@link module:controllers/PaymentInstruments~add|add} function with a clearform value of false.
   * - __error__ - calls the {@link module:controllers/PaymentInstruments~add|add} function with a clearform value of false.
   */
  function handlePaymentForm() {
      var paymentForm = app.getForm('paymentinstruments');
      paymentForm.handleAction({
          create: function () {
              if (!create()) {
                  add(false);
                  return;
              } else {
                  response.redirect(URLUtils.https('PaymentInstruments-List'));
              }
          },
          error: function () {
              add(false);
          }
      });
  }
  /**
   * Saves a  customer credit card payment instrument.
   * @param {Object} params
   * @param {dw.customer.CustomerPaymentInstrument} params.PaymentInstrument - credit card object.
   * @param {dw.web.FormGroup} params.CreditCardFormFields - new credit card form.
   */
  function save(params) {
      var saveCustomerCreditCard = require('app_storefront_core/cartridge/scripts/checkout/SaveCustomerCreditCard');
      var result = saveCustomerCreditCard.save(params);
      if (result === PIPELET_ERROR) {
          throw new Error('Problem saving credit card');
      }
  }
  
  /**
   * Creates a new payment instrument. Verifies the credit card and checks if it is a duplicate of
   * a card already in the current customer's payment instruments. In a transaction, the function
   * attempts to save the credit card to the customer's payment instruments. If a duplicate card was
   * detected, the original card is removed after the new card is created. If the card cannot be created
   * successfully, the transaction is rolled back. Whether successful or not, the paymentinstruments
   * form is cleared.
   *
   * @transaction
   * @return {boolean} true if the credit card can be verified, false otherwise
   */
  function create() {
      if (!verifyCreditCard()) {
          return false;
      }
  
      var paymentForm = app.getForm('paymentinstruments');
      var newCreditCardForm = paymentForm.get('creditcards.newcreditcard');
      var ccNumber = newCreditCardForm.get('number').value();
  
      var wallet = customer.getProfile().getWallet();
      var paymentInstruments = wallet.getPaymentInstruments(dw.order.PaymentInstrument.METHOD_CREDIT_CARD);
  
      var isDuplicateCard = false;
      var oldCard;
  
      for (var i = 0; i < paymentInstruments.length; i++) {
          var card = paymentInstruments[i];
          if (card.creditCardNumber === ccNumber) {
              isDuplicateCard = true;
              oldCard = card;
              break;
          }
      }
  
      Transaction.begin();
      var paymentInstrument = wallet.createPaymentInstrument(dw.order.PaymentInstrument.METHOD_CREDIT_CARD);
  
      try {
          save({
              PaymentInstrument: paymentInstrument,
              CreditCardFormFields: newCreditCardForm.object
          });
      } catch (err) {
          Transaction.rollback();
          return false;
      }
  
      if (isDuplicateCard) {
          wallet.removePaymentInstrument(oldCard);
      }
  
      Transaction.commit();
  
      paymentForm.clear();
  
      return true;
  }
  
  
  /**
   * Form handler for the paymentinstruments form. Handles the following actions:
   * - __remove__ - uses the form and action supplied by the FormModel to remove a customer payment instrument
   * in a transaction.
   * - __error__ - does nothing.
   *
   * In either case, redirects to the {@link module:controllers/PaymentInstruments~list|List} function.
   * @transaction
   * @TODO Should be moved into handlePaymentForm
   * @FIXME Inner method should be lowercase.error action should do something
   */
  function Delete() {
      var paymentForm = app.getForm('paymentinstruments');
      paymentForm.handleAction({
          remove: function (formGroup, action) {
              Transaction.wrap(function () {
                  var wallet = customer.getProfile().getWallet();
                  wallet.removePaymentInstrument(action.object);
              });
  
          },
          error: function () {
              // @TODO When could this happen
          }
      });
  
      response.redirect(URLUtils.https('PaymentInstruments-List'));
  }
  
  
  /*
   * Private helpers
   */
  
  /**
   * Verifies if the entered credit card details are valid.
   *
   * @returns {boolean} true in case of success, otherwise false.
   */
  function verifyCreditCard() {
      var newCreditCardForm = app.getForm('paymentinstruments.creditcards.newcreditcard');
  
      var expirationMonth = newCreditCardForm.get('expiration.month').value();
      var expirationYear = newCreditCardForm.get('expiration.year').value();
      var cardNumber = newCreditCardForm.get('number').value();
      var paymentCard = PaymentMgr.getPaymentCard(newCreditCardForm.get('type').value());
      var verifyPaymentCardResult = paymentCard.verify(expirationMonth, expirationYear, cardNumber);
  
      if (verifyPaymentCardResult.error === true) {
  
          if (!newCreditCardForm.isValid()) {
              return false;
          }
  
          if (verifyPaymentCardResult.code === Status.OK) {
              return true;
          }
  
          // Invalidate the payment card form elements.
          for (var i = 0; i < verifyPaymentCardResult.items.length; i++) {
              if (verifyPaymentCardResult.items[i].code === PaymentStatusCodes.CREDITCARD_INVALID_CARD_NUMBER) {
                  newCreditCardForm.get('number').invalidate();
              } else if (verifyPaymentCardResult.items[i].code === PaymentStatusCodes.CREDITCARD_INVALID_EXPIRATION_DATE) {
                  newCreditCardForm.get('expiration.month').invalidate();
                  newCreditCardForm.get('expiration.year').invalidate();
              }
          }
          return false;
      }
  
      return true;
  }
  
  /*
   * Web exposed methods
   */
  /** Renders a list of the saved credit card payment instruments of the current customer.
   * @see module:controllers/PaymentInstruments~list */
  exports.List = guard.ensure(['https', 'get', 'loggedIn'], list);
  /** Adds a new credit card payment instrument to the saved payment instruments of the current customer.
   * @see module:controllers/PaymentInstruments~add */
  exports.Add = guard.ensure(['https', 'get', 'loggedIn'], add);
  /** Handles the submitted form for creating payment instruments.
   * @see module:controllers/PaymentInstruments~handlePaymentForm */
  exports.PaymentForm = guard.ensure(['https', 'post', 'loggedIn', 'csrf'], handlePaymentForm);
  /** Deletes a saved credit card payment instrument.
   * @see module:controllers/PaymentInstruments~Delete */
  exports.Delete = guard.ensure(['https', 'loggedIn'], Delete);
  