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

      var CertificateSelfSignedCreateView = (function() {

          var FORM = '#cic-selfsigned-create-form',
              OK = "#cic-selfsigned-create-ok",
              CANCEL = '#cic-selfsigned-create-cancel',
              COUNTRY = '#cic-selfsigned-country',
              STATE = '#cic-selfsigned-state',
              LOCALITY = '#cic-selfsigned-locality',
              ORGANIZATION = '#cic-selfsigned-org',
              COMMON_NAME = '#cic-selfsigned-cn',
              ORGANIZATIONAL_UNIT = '#cic-selfsigned-ou',
              ALTERNATIVE_NAME = '#cic-selfsigned-alt-name',
              CONTACT_PERSON = '#cic-selfsigned-contact',
              EMAIL = '#cic-selfsigned-email',
              SURNAME = '#cic-selfsigned-surname',
              GIVEN_NAME = '#cic-selfsigned-given-name',
              INITIALS = '#cic-selfsigned-initials',
              DN_QUALIFIER = '#cic-selfsigned-dn',
              SIZE = 'size',
              COUNTRY_SIZE = '2',
              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,
              SPLASH_DIALOG = '#cic-selfsigned-create-splash-dialog',
              SPLASH_PROGRESS = '#cic-selfsigned-create-splash-progress',
              SPLASH_ESTIMATE = '#cic-selfsigned-create-splash-estimate',
              CREATE_COLLAPSIBLE = "#cic-selfsigned-collapsible",
              LOGGED_IN_MSG = '#cic-selfsigned-logged-in-msg',
              INVALID_PARAMETER_ERROR_CODE = 'INVALID_PARAMETER';

          var RELOAD_TIMEOUT = 15000; //msec
          var validator;
         

        /**
         * Constructor
         */
        function CertificateSelfSignedCreateView() {

          var formStateView = new FormStateView();
          // Splash dialog timer
          var splashTimer;

          // Display an error in the dialog Notifications view.
          function displayDialogNotification(action, source, message, details) {
              var notif = {
                  summary : action,
                  sourceName : source,
                  uri : 'create-selfsignedcertificate-error' + (new Date()).getTime(),
                  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 failureNotificationDetails = "";
              var resolution = "";
              var supportDumpLink = '<a href=\'#/settings/support/createSupportDump\'>' +
                          localizer.getString('fs.settings.certificate.selfsigned.error.support_dump_link') + '</a>';
              var supportDumpLinkMsg =
                           localizer.getString('fs.settings.certificate.selfsigned.error.support_dump_link_message',
                           [supportDumpLink]);
              // error from the task tracker in the form of an array
              if ($.isArray(errorInfo))
              {
                  for(var i=0; i < errorInfo.length; i++) {
                      var serverError = errorInfo[i];
                          resolution = serverError.recommendedActions ? '</br>' +
                                  serverError.recommendedActions.join(' ') : '';
                          failureNotificationDetails += serverError.message + resolution + '</br>';
                      }
              }
              // error directly from the REST request
              else
              {
                  var message = (errorInfo.message === null) ? '' : errorInfo.message;
                  resolution = errorInfo.recommendedActions ? '</br>' + errorInfo.recommendedActions.join(' ') : '';
                  failureNotificationDetails = message + resolution + '</br>';
              }
              failureNotificationDetails += '</br>' + supportDumpLinkMsg;

              return failureNotificationDetails;
          }

          /**
           * Dismiss the splash dialog box.
           */
          function dismissDialog() {
              if(splashTimer) {
                  clearTimeout(splashTimer);
                  splashTimer = null;
                  $(SPLASH_DIALOG).dialog('close');
              }
          }

          /**
           * When the createSelfSignedCertificate completes successfully,
           * dismiss the dialog and notify the caller of success.
           */
          function onCreateCertSuccess() {
              var notif = {
                  uri : 'create-CSR-success' + (new Date()).getTime(),
                  summary : localizer.getString('fs.settings.certificate.selfsigned.create_action'),
                  sourceName : localizer.getString('fs.settings.certificate.selfsigned.source_object'),
                  status : 'ok',
                  details : localizer.getString('fs.settings.certificate.selfsigned.create_success')
              };
              notifications.add(notif, true);
              formStateView.setMessage(notif);

              setTimeout( function() {
                  dismissDialog();
                  var notPersistent = false;
                  session.setProperty("navToSettingsOverview","true",notPersistent);
                  router.reload();
              }, RELOAD_TIMEOUT );
          }

          /**
           * When the createSelfSignedCertificate call returns
           * success, display the splash dialog.
           */
          function onCreateCertRunning(timeout) {
              $(SPLASH_DIALOG).dialog('open');

              // update progress
              $(SPLASH_PROGRESS).hpProgressBar(0);
              var done = 0;
              var remaining = 0;
              $(SPLASH_ESTIMATE).text(localizer.getString('fs.settings.certificate.selfsigned.dialog.remaining',
                      [timeout/1000]));
              splashTimer = setInterval(function() {
                  done += 1000;
                  remaining = timeout-done;
                  if( remaining < 0 )
                  {
                      remaining = 0 ;
                  }
                  $(SPLASH_ESTIMATE).text(localizer.getString('fs.settings.certificate.selfsigned.dialog.remaining',
                          [remaining/1000]));
                  $(SPLASH_PROGRESS).hpProgressBar((done * 100) / timeout);
              }, 1000);
          }

          /**
           * @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;

              switch (sourceId) {
                  case 'Country':
                      $.extend(validatorErrors, {
                          'country' : errorMessage
                      });
                      break;
                  case 'State or Province' :
                      $.extend(validatorErrors, {
                          'state' : errorMessage
                      });
                      break;
                  case 'City or Locality' :
                      $.extend(validatorErrors, {
                          'locality' : errorMessage
                      });
                      break;
                  case 'OrganizationName' :
                      $.extend(validatorErrors, {
                          'organization' : errorMessage
                      });
                      break;
                  case 'CommonName' :
                      $.extend(validatorErrors, {
                          'cn' : errorMessage
                      });
                      break;
                  case 'OrganizationalUnit' :
                      $.extend(validatorErrors, {
                          'ou' : errorMessage
                      });
                      break;
                  case 'EmailAddress' :
                      $.extend(validatorErrors, {
                          'email' : errorMessage
                      });
                      break;
                  case 'ContactPerson' :
                      $.extend(validatorErrors, {
                          'contact' : errorMessage
                      });
                      break;
                  case 'DNQualifier' :
                      $.extend(validatorErrors, {
                          'dn' : errorMessage
                      });
                      break;
                  case 'GivenName' :
                      $.extend(validatorErrors, {
                          'given_name' : errorMessage
                      });
                      break;
                  case 'Initials' :
                      $.extend(validatorErrors, {
                          'initials' : errorMessage
                      });
                      break;
                  case 'Surname' :
                      $.extend(validatorErrors, {
                          'surname' : errorMessage
                      });
                      break;
                  case 'AlternateName' :
                      $.extend(validatorErrors, {
                          'alt_name' : errorMessage
                      });
                      break;
                  default:
                      break;
              }
          }

          /**
           * @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 createSelfSignedCertificate call returns an error,
           * dismiss the splash dialog and display an error
           *
           * @param errorInfo
           */
          function onCreateCertError(errorInfo) {
              dismissDialog();

              // 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.selfsigned.create_action');
              var source = localizer.getString('fs.settings.certificate.selfsigned.source_object');
              var applyErrorMsg = localizer.getString('fs.settings.certificate.selfsigned.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.selfsigned.get_action');
        	  var source = localizer.getString('fs.settings.certificate.selfsigned.source_object');
        	  var applyErrorMsg = localizer.getString('fs.settings.certificate.selfsigned.get_error');
              var failureNotificationDetails = formatNotificationDetails(errorInfo);
              displayDialogNotification(action, source, applyErrorMsg, failureNotificationDetails);
          }

          /**
           * Display the list of logged in users
           * @returns number of logged in users
           */
          function updateOtherUsersLoggedIn() {
              var list = presenter.getOtherUsersLoggedInList();
              var count = list.length;
              for ( var i = 0; i < list.length; i++) {
                  $('.hp-dependencies', FORM).append('<li>' + list[i] + '</li>');
              }
              if (count > 1) {
                  $(LOGGED_IN_MSG).text(localizer.getString(
                                'fs.settings.certificate.selfsigned.dialog.usersLoggedIn', [ count ]));
              } else if (count == 1) {
                  $(LOGGED_IN_MSG).text(localizer.getString(
                                'fs.settings.certificate.selfsigned.dialog.oneUserLoggedIn'));
              } else if (count == 0) {
                  $(LOGGED_IN_MSG).text('');
              }
              return count;
            }

          /**
           * Handler to get the list of logged in users
           */
          function onUsersLoggedInSuccess() {
              updateOtherUsersLoggedIn();
          }

          /**
           * If we have an error retrieving the logged in list, don't display
           * the logged in message
           * @param errorInfo
           */
          function onUsersLoggedInError(errorInfo){
            $(LOGGED_IN_MSG).text('');
          }

          /**
           * Register events
           */
          function registerEvents() {
              presenter.on("usersLoggedInSuccess", onUsersLoggedInSuccess);
              presenter.on("usersLoggedInError", onUsersLoggedInError);
          }

          /**
           * Unregister events
           */
          function removeEvents() {
              presenter.off("usersLoggedInSuccess", onUsersLoggedInSuccess);
              presenter.off("usersLoggedInError", onUsersLoggedInError);
          }

          /**
           * 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);
              // Initialize the initialValues array in FormStateView with form values
              formStateView.reset();
          };

          /**
           * 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 createSelfSignedCertificate = 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()
              };
              var unitTest = false;
              presenter.createSelfSignedCertificate(certObject, unitTest, {
                  running : onCreateCertRunning,
                  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 : {
                        certAltMaxLength : ALTERNATIVE_NAME,
                        certAltNameHostOrIpChars : 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
                      }
                  }
              });

              /*
               * Validator for alternative name field syntax.
               * Allow the characters of a hostname or IPv4/IPv6 address
               * plus spaces and commas.
               */
              jQuery.validator.addMethod("certAltNameHostOrIpChars", 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("certAltMaxLength", 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.certAltMaxLength =
                                  localizer.getString('fs.settings.certificate.validator.max_length',
                                          [MAX_ALT_NAME_LENGTH]);
                          }
                          else
                          {
                              // user has not specified the CN
                              $.validator.messages.certAltMaxLength =
                                  localizer.getString('fs.settings.certificate.validator.max_length_no_cn',
                                          [MAX_ALT_NAME_LENGTH]);
                          }
                          return false;
                      }
                      else
                      {
                          return true;
                      }
                  }, '');
          }

          function validateFormAndCreateCertificate() {
              if ($(FORM).valid()) {
                  createSelfSignedCertificate();
              }
          };

          /**
           * @public
           */
          this.init = function() {
              registerEvents();

              // perform validation
              initForm();

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

              // 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();
              presenter.usersLoggedIn();
              $(CREATE_COLLAPSIBLE).hpCollapsible();

              $(OK).click(function(ev) {
                  validateFormAndCreateCertificate();
                  ev.preventDefault();
              });

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

              $(SPLASH_DIALOG).dialog({
                autoOpen : false,
                  modal : true,
                  position : "center",
                  draggable : false,
                  resizable : false,
                  show : 'fade',
                  dialogClass : 'hp-splash-dialog',
                  width : 400
              });
          };

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

          /**
           * @public
           */
          this.pause = function() {
              removeEvents();
              $('.hp-dependencies', FORM).empty();
          };

        }

        return new CertificateSelfSignedCreateView();
    }());

    return CertificateSelfSignedCreateView;
});
