// (C) Copyright 2020 Hewlett-Packard Enterprise Company, L.P.
define(
    [ 'fs/presenter/settings/CertificatePresenter',
      'hp/view/FormStateView',
      'hp/core/Notifications',
      'hp/core/Localizer',
      'jquery',
      'hp/lib/jquery.hpCollapsible',
      'lib/jquery.validate' ],
function(presenter, FormStateView, notifications, localizer) { "use strict";

      var CSRCreateView = (function() {

          var FORM = '#cic-csr-create-form',
              OK = "#cic-csr-create-ok",
              CANCEL = '#cic-csr-create-cancel',
              COUNTRY = '#cic-csr-country',
              STATE = '#cic-csr-state',
              LOCALITY = '#cic-csr-locality',
              ORGANIZATION = '#cic-csr-org',
              COMMON_NAME = '#cic-csr-cn',
              ORGANIZATIONAL_UNIT = '#cic-csr-ou',
              ALTERNATIVE_NAME = '#cic-csr-alt-name',
              CONTACT_PERSON = '#cic-csr-contact',
              EMAIL = '#cic-csr-email',
              SURNAME = '#cic-csr-surname',
              GIVEN_NAME = '#cic-csr-given-name',
              INITIALS = '#cic-csr-initials',
              DN_QUALIFIER = '#cic-csr-dn',
              SIZE = 'size',
              COUNTRY_SIZE = '2',
              CHALLENGE_PASSWD = '#cic-csr-challenge-password',
              CONFIRM_PASSWD = '#cic-csr-confirm-password',
              UNSTRUCTURED_NAME = '#cic-csr-unstructured-name',
              MAX_COUNTRY_LENGTH = 2,
              MAX_STATE_LENGTH = 128,
              MAX_LOCALITY_LENGTH = 128,
              MAX_ORG_LENGTH = 64,
              MAX_OU_LENGTH = 64,
              MAX_CN_LENGTH = 64,
              MAX_ALT_NAME_LENGTH = 2048,
              MAX_CONTACT_LENGTH = 64,
              MAX_EMAIL_LENGTH = 128,
              MAX_SURNAME_LENGTH = 64,
              MAX_GIVEN_NAME_LENGTH = 64,
              MAX_INITIALS_LENGTH = 20,
              MAX_DN_LENGTH = 128,
              MAX_UNSTRUCTURED_NAME_LENGTH = 64,
              MIN_PASSWORD_LENGTH = 8,
              BASE64_SAVE = '#cic-csr-base64-save',
              BASE64_TEXT = '#cic-csr-base64-text',
              INVALID_PARAMETER_ERROR_CODE = 'INVALID_PARAMETER';

          var validator;

        /**
         * Constructor
         */
        function CSRCreateView() {

          var formStateView = new FormStateView();

          // Display an error in the dialog Notifications view.
          function displayDialogNotification(action, source, message, details) {
              var notif = {
                      summary : action,
                      uri : 'create-CSR-error' + (new Date()).getTime(),
                      sourceName : source,
                      status : 'error',
                      details : message + '\n' + details
                  };
                  formStateView.setMessage(notif);
                  notifications.add(notif, true);
          }

          /**
           * @private Formats error information from a REST request
           * @param errorInfo {object} Rest request error information
           * @returns {String} Formatted error information to display
           */
          function formatNotificationDetails(errorInfo) {
              var resolution = (errorInfo.recommendedActions && errorInfo.recommendedActions !== '?') ? '</br>' +
                      errorInfo.recommendedActions : '';
              var supportDumpLink = '<a href=\'#/settings/support/createSupportDump\'>' +
                  localizer.getString('fs.settings.certificate.csr.error.support_dump_link') + '</a>';
              var supportDumpLinkMsg =
                localizer.getString('fs.settings.certificate.csr.error.support_dump_link_message',
                [supportDumpLink]);
              var failureNotificationDetails = '</br>' + errorInfo.message + resolution + '</br></br>' +
                  supportDumpLinkMsg;
              return failureNotificationDetails;
          }

          /**
           * When the createCSR completes successfully,
           * bring up the dialog and notify the caller of success.
           */
          function onCreateCertSuccess(value) {
            $(BASE64_SAVE).dialog('open');
            //Adding a check to see if the line separator before the "END CERTIFICATE REQUEST" is ignored by the browser.
            //If ignored adding it.
            var index = value.base64Data.indexOf('-----END CERTIFICATE');
            if(index != -1){
                if(value.base64Data[index-1] != '\n'){
                    value.base64Data = value.base64Data.replace('-----END CERTIFICATE REQUEST-----','\r\n-----END CERTIFICATE REQUEST-----');
                }
            }
            $(BASE64_TEXT).val(value.base64Data);
            $(BASE64_TEXT).focus();
            $(BASE64_TEXT).select();
            var notif = {
                uri : 'create-CSR-success' + (new Date()).getTime(),
                summary : localizer.getString('fs.settings.certificate.csr.create_action'),
                sourceName : localizer.getString('fs.settings.certificate.csr.source_object'),
                status : 'ok',
                details : localizer.getString('fs.settings.certificate.csr.create_success')
            };
            notifications.add(notif, true);
            formStateView.setMessage(notif);
          }

          /**
          * @private Add a server side error to the list of errors that the validator will display
          * @param {object} validatorErrors The list of errors that the validator will display
          * @param {object} errorFromServer The error from a REST response body to add to the list
          */
          function addValidatorError(validatorErrors, errorFromServer) {
              var errorMessage = errorFromServer.message;
              var sourceId = errorFromServer.errorSource;

              var sourceMap = {
                      'Country': 'country',
                      'State or Province' : 'state',
                      'City or Locality' : 'locality',
                      'OrganizationName' : 'organization',
                      'CommonName' : 'cn',
                      'OrganizationalUnit' : 'ou',
                      'EmailAddress' : 'email',
                      'ContactPerson' : 'contact',
                      'DNQualifier' : 'dn',
                      'GivenName' : 'given_name',
                      'Initials' : 'initials',
                      'Surname' : 'surname',
                      'AlternateName' : 'alt_name',
                      'UnstructuredName' : 'unstructured_name',
                      'ChallengePassword' : 'challenge_password'
                  };

              if (sourceMap.hasOwnProperty(sourceId)) {
                  validatorErrors[sourceMap[sourceId]] = errorMessage;
              }
          }

          /**
          * @private Display invalid parameter errors from the server
          * @param {object} errorInfo The response body generated by a server exception
          */
          function handleParameterErrorsFromServer(errorInfo) {
            var validatorErrors = {};
            var serverErrorList = errorInfo.nestedErrors;
            var serverError;
            var handled = true;
            for(serverError in serverErrorList) {
              if(!addValidatorError(validatorErrors, serverErrorList[serverError])) {
                    handled = false;
                }
            }
            validator.showErrors(validatorErrors);

          }

          /**
           * When the createCSR call returns an error,
           * display the error
           *
           * @param errorInfo
           */
          function onCreateCertError(errorInfo) {
            // handle possible multiple invalid param exceptions from the backend
            if(errorInfo.errorCode == INVALID_PARAMETER_ERROR_CODE) {
              handleParameterErrorsFromServer(errorInfo);
              }

              var action = localizer.getString('fs.settings.certificate.csr.create_action');
              var source = localizer.getString('fs.settings.certificate.csr.source_object');
              var applyErrorMsg = localizer.getString('fs.settings.certificate.csr.create_error');
              var failureNotificationDetails = formatNotificationDetails(errorInfo);
              displayDialogNotification(action, source, applyErrorMsg, failureNotificationDetails);
          }

          /**
           * When the getCertificate call returns an error, display it
           *
           * @param errorInfo
           */
          function onGetCertError(errorInfo) {
              var action = localizer.getString('fs.settings.certificate.csr.get_action');
              var source = localizer.getString('fs.settings.certificate.csr.source_object');
              var applyErrorMsg = localizer.getString('fs.settings.certificate.csr.get_error');
              var failureNotificationDetails = formatNotificationDetails(errorInfo);
              displayDialogNotification(action, source, applyErrorMsg, failureNotificationDetails);
          }

          /**
           * Display the certificate data (called when it is received from the server).
           */
          var showCertificateData = function(certObject) {
              $(COUNTRY).val(certObject.country);
              $(STATE).val(certObject.state);
              $(LOCALITY).val(certObject.locality);
              $(ORGANIZATION).val(certObject.organization);
              $(COMMON_NAME).val(certObject.commonName);
              $(ORGANIZATIONAL_UNIT).val(certObject.organizationalUnit);
              $(ALTERNATIVE_NAME).val(certObject.alternativeName);
              $(CONTACT_PERSON).val(certObject.contactPerson);
              $(EMAIL).val(certObject.email);
              $(SURNAME).val(certObject.surname);
              $(GIVEN_NAME).val(certObject.givenName);
              $(INITIALS).val(certObject.initials);
              $(DN_QUALIFIER).val(certObject.dnQualifier);
              if(certObject.challengePassword)
              {
                  $(CHALLENGE_PASSWD).val("********");
                  $(CONFIRM_PASSWD).val("********");
              }
              else
              {
                  //don't show password mask during csr creation.
                  $(CHALLENGE_PASSWD).val("");
                  $(CONFIRM_PASSWD).val("");
              }
              
              $(UNSTRUCTURED_NAME).val(certObject.unstructuredName);
          };

          /**
           * Display the certificate data (called when it is received from the server).
           */
          var getCertificateData = function() {
              presenter.getCertificate({
                  success: showCertificateData,
                  error: onGetCertError
              });
          };

          /**
           * Create the certificate
           */
          var createCSR = function() {
              var certObject = {
                  country : $(COUNTRY).val(),
                  state : $(STATE).val(),
                  locality : $(LOCALITY).val(),
                  organization : $(ORGANIZATION).val(),
                  commonName : $(COMMON_NAME).val(),
                  organizationalUnit : $(ORGANIZATIONAL_UNIT).val(),
                  alternativeName : $(ALTERNATIVE_NAME).val(),
                  contactPerson : $(CONTACT_PERSON).val(),
                  email : $(EMAIL).val(),
                  surname : $(SURNAME).val(),
                  givenName : $(GIVEN_NAME).val(),
                  initials : $(INITIALS).val(),
                  dnQualifier : $(DN_QUALIFIER).val(),
                  unstructuredName : $(UNSTRUCTURED_NAME).val(),
                  challengePassword : $(CHALLENGE_PASSWD).val()
              };

              presenter.createCSR(certObject, {
                  success : onCreateCertSuccess,
                  error : function(errorInfo) {
                      onCreateCertError(errorInfo);
                  }
              });
          };

          /**
           * validate the certificate fields
           */
          function initForm() {
              $(COUNTRY).attr(SIZE, COUNTRY_SIZE);

              validator = $(FORM).validate({
                  rules : {
                      country : {
                          required : true,
                          maxlength : MAX_COUNTRY_LENGTH,
                          minlength : MAX_COUNTRY_LENGTH
                      },
                      state : {
                          required : true,
                          maxlength : MAX_STATE_LENGTH
                      },
                      locality : {
                          required : true,
                          maxlength : MAX_LOCALITY_LENGTH
                      },
                      organization : {
                          required : true,
                          maxlength : MAX_ORG_LENGTH
                      },
                      cn  : {
                          required : true,
                          hostAddress : ['hostname', 'fqdn', 'ipv6'],
                          maxlength : MAX_CN_LENGTH
                      },
                      ou : {
                        maxlength : MAX_OU_LENGTH
                      },
                      alt_name : {
                        cicCSRAltMaxLength : ALTERNATIVE_NAME,
                        cicCSRAltNameHostOrIpChars : ALTERNATIVE_NAME
                      },
                      contact : {
                        maxlength : MAX_CONTACT_LENGTH
                      },
                      email : {
                        email : EMAIL,
                        maxlength : MAX_EMAIL_LENGTH
                      },
                      surname : {
                        maxlength : MAX_SURNAME_LENGTH
                      },
                      given_name : {
                        maxlength : MAX_GIVEN_NAME_LENGTH
                      },
                      initials : {
                        maxlength : MAX_INITIALS_LENGTH
                      },
                      dn : {
                        maxlength : MAX_DN_LENGTH
                      },
                      unstructured_name : {
                        maxlength : MAX_UNSTRUCTURED_NAME_LENGTH
                      },
                      challenge_password : {
                          minlength : MIN_PASSWORD_LENGTH,
                          cicCSRCheckPasswdString : CHALLENGE_PASSWD
                      },
                      confirm_password : {
                        cicCSRMatchPasswd : CHALLENGE_PASSWD
                      }
                  }
              });

              /*
               * Validator for password field.
               * Make sure the challenge and confirm passwords match.
               */
              jQuery.validator.addMethod("cicCSRMatchPasswd", function(value, element, param) {
                  // bind to the blur event of the target in order to revalidate whenever the target field is updated
                  var target = $(param).unbind(".validate-equalTo")
                      .bind("blur.validate-equalTo", function() {
                          $(element).valid();
                      });
                  return value == target.val();
                  },
                  localizer.getString('fs.settings.certificate.validator.matchpasswd'));

              //No special characters allowed <>;,"'&\/|+.:=
              jQuery.validator.addMethod("cicCSRCheckPasswdString", function(
                  value, element, param) {
                  //field is not required
                  if (!value) {
                      return true;
                  }
                  return (/^[^<>;,"'&\\\/|+:= ]+$/.test(value));
                  },
                  localizer.getString('fs.settings.certificate.validator.passwdvalidation'));

              /*
               * Validator for alternative name field.
               * Allow the characters of a hostname or IPv4/IPv6 address
               * plus spaces and commas.
               */
              jQuery.validator.addMethod("cicCSRAltNameHostOrIpChars", function(
                      value, element, param) {
                  // field is not required
                  if (!value) {
                      return true;
                  }
                  return (/^[a-zA-Z0-9_.:, \-]*$/.test(value));
                  },
                  localizer.getString('fs.settings.certificate.validator.host_or_ip_required'));

              /*
               * Validator for alternative name field max length.
               * Ensure there is enough space for the backend to add on the CN
               * if it was not already specified as part of the alternative name.
               */
              jQuery.validator.addMethod("cicCSRAltMaxLength", function(
                      value, element, param) {
                      // field is not required
                      if (!value) {
                          return true;
                      }

                      if (value.length > MAX_ALT_NAME_LENGTH)
                      {
                          if (value.indexOf($(COMMON_NAME).val()) != -1)
                          {
                              // user specified the CN
                              $.validator.messages.cicCSRAltMaxLength =
                                  localizer.getString('fs.settings.certificate.validator.max_length',
                                          [MAX_ALT_NAME_LENGTH]);
                          }
                          else
                          {
                              // user has not specified the CN
                              $.validator.messages.cicCSRAltMaxLength =
                                  localizer.getString('fs.settings.certificate.validator.max_length_no_cn',
                                          [MAX_ALT_NAME_LENGTH]);
                          }
                          return false;
                      }
                      else
                      {
                          return true;
                      }
                  }, '');

          }

          /**
           * @public
           */
          this.init = function() {
              var updateConfirmButtons = {};

              // perform validation
              initForm();

              formStateView.init({
                form: FORM,
                autoTrack: true
              });

              updateConfirmButtons[localizer.getString('fs.settings.certificate.csr.ok_button')] =
                  function() {
                      $(BASE64_SAVE).dialog('close');
                      $(CANCEL).trigger('click');
                  };

              // pre-populate the form with data from current certificate, if one exists
              getCertificateData();

              // reset and then obtain list of logged in users for the warning dialog
              presenter.init();

              // Handlers for the buttons.
              $(OK).click(function(ev) {
                  if ($(FORM).valid()) {
                    createCSR();
                  }
                ev.preventDefault();
              });

              $(CANCEL).click(function(ev) {
                  validator.resetForm();
                  formStateView.reset();
              });

              $(BASE64_SAVE).dialog({
                  autoOpen : false,
                  draggable : false,
                  modal : true,
                  resizable : false,
                  position: "center",
                  dialogClass : 'hp-simple-dialog',
                  buttons : updateConfirmButtons,
                  width : 900
              });
          };

          /**
           * @public
           */
          this.resume = function() {
              getCertificateData();
              formStateView.reset();
              validator.resetForm();
          };

          /**
           * @public
           */
          this.pause = function() {
              formStateView.reset();
              validator.resetForm();
          };

        }

        return new CSRCreateView();
    }());

    return CSRCreateView;
});
