/home/ivoiecob/email.hirewise-va.com/modules/TwoFactorAuth/js/popups/VerifySecondFactorPopup.js
'use strict'

const _ = require('underscore'),
  ko = require('knockout')

const TextUtils = require('%PathToCoreWebclientModule%/js/utils/Text.js'),
  Utils = require('%PathToCoreWebclientModule%/js/utils/Common.js'),
  Ajax = require('%PathToCoreWebclientModule%/js/Ajax.js'),
  Api = require('%PathToCoreWebclientModule%/js/Api.js'),
  App = require('%PathToCoreWebclientModule%/js/App.js'),
  CAbstractPopup = require('%PathToCoreWebclientModule%/js/popups/CAbstractPopup.js'),
  Screens = require('%PathToCoreWebclientModule%/js/Screens.js')

const ConvertUtils = require('modules/%ModuleName%/js/utils/Convert.js'),
  DeviceUtils = require('modules/%ModuleName%/js/utils/Device.js'),
  Settings = require('modules/%ModuleName%/js/Settings.js'),
  TwoFactorApi = require('modules/%ModuleName%/js/utils/Api.js')

/**
 * @constructor
 */
function CVerifySecondFactorPopup() {
  CAbstractPopup.call(this)

  this.isMobile = ko.observable(App.isMobile() || false)

  this.afterVerifyCallback = () => {}
  this.fOnCancel = null
  this.login = ko.observable(null)
  this.sPassword = null

  this.bAllowTrustedDevices = Settings.AllowTrustedDevices

  this.verificationResponse = ko.observable(null)
  this.verificationPassed = ko.computed(function () {
    return this.verificationResponse() !== null
  }, this)
  this.verificationResponse.subscribe(function () {
    if (this.verificationPassed() && !this.bAllowTrustedDevices) {
      this.afterVerify()
      this.closePopup()
    }
  }, this)

  this.trustThisBrowser = ko.observable(false)
  this.sTrustThisBrowserText = TextUtils.i18n(
    '%MODULENAME%/LABEL_TRUST_DEVICE_PLURAL',
    {
      COUNT: Settings.TrustDevicesForDays,
    },
    null,
    Settings.TrustDevicesForDays
  )

  this.allOptionsVisible = ko.observable(false)
  this.securityKeyVisible = ko.observable(false)
  this.authenticatorAppVisible = ko.observable(false)
  this.backupCodesVisible = ko.observable(false)

  this.hasSecurityKey = ko.observable(false)
  this.securityKeyInProgress = ko.observable(false)
  this.securityKeyError = ko.observable(false)
  this.bSecurityKeysNotSupportedError = !(navigator.credentials && navigator.credentials.get)
  this.bIsHttps = window.location.protocol === 'https:'

  this.hasAuthenticatorApp = ko.observable(false)
  this.authenticatorCode = ko.observable('')
  this.authenticatorCodeFocused = ko.observable(false)
  this.authenticatorCodeInProgress = ko.observable(false)

  this.hasBackupCodes = ko.observable(false)
  this.backupCode = ko.observable(false)
  this.backupCodeFocus = ko.observable(false)
  this.backupCodeInProgress = ko.observable(false)

  this.hasSeveralOptions = ko.computed(function () {
    var iOptionsCount = 0
    if (this.hasSecurityKey()) {
      iOptionsCount++
    }
    if (this.hasAuthenticatorApp()) {
      iOptionsCount++
    }
    if (this.hasBackupCodes()) {
      iOptionsCount++
    }
    return iOptionsCount > 1
  }, this)

  this.continueInProgress = ko.observable(false)
  this.continueCommand = Utils.createCommand(this, this.afterVerify, function () {
    return !this.continueInProgress()
  })
}

_.extendOwn(CVerifySecondFactorPopup.prototype, CAbstractPopup.prototype)

CVerifySecondFactorPopup.prototype.PopupTemplate = '%ModuleName%_VerifySecondFactorPopup'

CVerifySecondFactorPopup.prototype.onOpen = function (
  afterVerifyCallback,
  fOnCancel,
  oTwoFactorAuthData,
  sLogin,
  sPassword
) {
  this.continueInProgress(false)

  this.afterVerifyCallback = typeof afterVerifyCallback === 'function' ? afterVerifyCallback : () => {}
  this.fOnCancel = fOnCancel
  this.login(sLogin)
  this.sPassword = sPassword

  this.hasSecurityKey(Settings.AllowSecurityKeys && oTwoFactorAuthData.HasSecurityKey)
  this.hasAuthenticatorApp(Settings.AllowAuthenticatorApp && oTwoFactorAuthData.HasAuthenticatorApp)
  this.hasBackupCodes(Settings.AllowBackupCodes && oTwoFactorAuthData.HasBackupCodes)

  this.verificationResponse(null)
  this.authenticatorCode('')
  this.authenticatorCodeInProgress(false)
  this.backupCode('')
  this.backupCodeInProgress(false)

  this.allOptionsVisible(false)
  this.securityKeyVisible(false)
  this.authenticatorAppVisible(false)
  this.backupCodesVisible(false)
  if (this.hasSecurityKey()) {
    this.useSecurityKey()
  } else if (this.hasAuthenticatorApp()) {
    this.useAuthenticatorApp()
  }
}

CVerifySecondFactorPopup.prototype.useOtherOption = function () {
  this.allOptionsVisible(true)
  this.securityKeyVisible(false)
  this.authenticatorAppVisible(false)
  this.backupCodesVisible(false)
}

CVerifySecondFactorPopup.prototype.useSecurityKey = function () {
  if (this.hasSecurityKey()) {
    this.allOptionsVisible(false)
    this.securityKeyVisible(true)
    this.authenticatorAppVisible(false)
    this.backupCodesVisible(false)
    this.verifySecurityKey()
  }
}

CVerifySecondFactorPopup.prototype.useAuthenticatorApp = function () {
  if (this.hasAuthenticatorApp()) {
    this.allOptionsVisible(false)
    this.securityKeyVisible(false)
    this.authenticatorAppVisible(true)
    this.backupCodesVisible(false)
    this.authenticatorCodeFocused(true)
  }
}

CVerifySecondFactorPopup.prototype.useBackupCodes = function () {
  if (this.hasBackupCodes()) {
    this.allOptionsVisible(false)
    this.securityKeyVisible(false)
    this.authenticatorAppVisible(false)
    this.backupCodesVisible(true)
    this.backupCodeFocus(true)
  }
}

CVerifySecondFactorPopup.prototype.verifySecurityKey = function () {
  if (!this.bSecurityKeysNotSupportedError) {
    var oParameters = {
      Login: this.login(),
      Password: this.sPassword,
    }
    this.securityKeyInProgress(true)
    this.securityKeyError(false)
    Ajax.send('%ModuleName%', 'VerifySecurityKeyBegin', oParameters, this.onVerifySecurityKeyBegin, this)
  } else {
    this.securityKeyInProgress(false)
    this.securityKeyError(true)
  }
}

CVerifySecondFactorPopup.prototype.onVerifySecurityKeyBegin = function (oResponse) {
  var oGetArgs = oResponse && oResponse.Result
  if (oGetArgs) {
    oGetArgs.publicKey.challenge = ConvertUtils.base64ToArrayBuffer(oGetArgs.publicKey.challenge)
    oGetArgs.publicKey.allowCredentials.forEach((element) => {
      element.id = ConvertUtils.base64ToArrayBuffer(element.id)
    })

    navigator.credentials
      .get(oGetArgs)
      .then((oCreds) => {
        var oCredsResponse = oCreds && oCreds.response,
          oParameters = {
            Login: this.login(),
            Password: this.sPassword,
            Attestation: {
              id: oCreds && oCreds.rawId ? ConvertUtils.arrayBufferToBase64(oCreds.rawId) : null,
              clientDataJSON:
                oCredsResponse && oCredsResponse.clientDataJSON
                  ? ConvertUtils.arrayBufferToBase64(oCredsResponse.clientDataJSON)
                  : null,
              authenticatorData:
                oCredsResponse && oCredsResponse.authenticatorData
                  ? ConvertUtils.arrayBufferToBase64(oCredsResponse.authenticatorData)
                  : null,
              signature:
                oCredsResponse && oCredsResponse.signature
                  ? ConvertUtils.arrayBufferToBase64(oCredsResponse.signature)
                  : null,
            },
          }
        Ajax.send('%ModuleName%', 'VerifySecurityKeyFinish', oParameters, this.onVerifySecurityKeyFinish, this)
      })
      .catch((err) => {
        this.securityKeyInProgress(false)
        this.securityKeyError(true)
      })
  } else {
    this.securityKeyInProgress(false)
    this.securityKeyError(true)
    Api.showErrorByCode(oResponse, TextUtils.i18n('%MODULENAME%/ERROR_VERIFY_SECURITY_KEY'))
  }
}

CVerifySecondFactorPopup.prototype.onVerifySecurityKeyFinish = function (oResponse) {
  this.securityKeyInProgress(false)
  if (oResponse && oResponse.Result) {
    this.verificationResponse(oResponse)
  } else {
    this.securityKeyError(true)
    Api.showErrorByCode(oResponse, TextUtils.i18n('%MODULENAME%/ERROR_VERIFY_SECURITY_KEY'))
  }
}

CVerifySecondFactorPopup.prototype.verifyAuthenticatorCode = function () {
  var oParameters = {
    Login: this.login(),
    Password: this.sPassword,
    Code: this.authenticatorCode(),
  }
  this.authenticatorCodeInProgress(true)
  Ajax.send('%ModuleName%', 'VerifyAuthenticatorAppCode', oParameters, this.onVerifyAuthenticatorAppCodeResponse, this)
}

CVerifySecondFactorPopup.prototype.onVerifyAuthenticatorAppCodeResponse = function (oResponse) {
  var oResult = oResponse.Result

  this.authenticatorCodeInProgress(false)
  this.authenticatorCode('')
  if (oResult) {
    this.verificationResponse(oResponse)
  } else {
    Screens.showError(TextUtils.i18n('%MODULENAME%/ERROR_WRONG_CODE'))
  }
}

CVerifySecondFactorPopup.prototype.verifyBackupCode = function () {
  var oParameters = {
    Login: this.login(),
    Password: this.sPassword,
    BackupCode: this.backupCode(),
  }
  this.backupCodeInProgress(true)
  Ajax.send('%ModuleName%', 'VerifyBackupCode', oParameters, this.onVerifyBackupCode, this)
}

CVerifySecondFactorPopup.prototype.onVerifyBackupCode = function (oResponse) {
  var oResult = oResponse.Result

  this.backupCodeInProgress(false)
  this.backupCode('')
  if (oResult) {
    this.verificationResponse(oResponse)
  } else {
    Screens.showError(TextUtils.i18n('%MODULENAME%/ERROR_WRONG_BACKUP_CODE'))
  }
}

CVerifySecondFactorPopup.prototype.cancelPopup = function () {
  if (_.isFunction(this.fOnCancel)) {
    this.fOnCancel(false)
  }
  this.closePopup()
}

CVerifySecondFactorPopup.prototype.afterVerify = function () {
  const authToken =
    (this.verificationResponse() &&
      this.verificationResponse().Result &&
      this.verificationResponse().Result.AuthToken) ||
    ''
  TwoFactorApi.saveDevice(authToken, () => {
    this.trustDevice(authToken, () => {
      this.afterVerifyCallback(this.verificationResponse())
    })
  })
}

CVerifySecondFactorPopup.prototype.trustDevice = function (authToken, successCallback) {
  if (!Settings.AllowUsedDevices || !this.trustThisBrowser()) {
    successCallback()
    return
  }

  const parameters = {
    DeviceId: App.getCurrentDeviceId(),
    DeviceName: DeviceUtils.getName(),
    Trust: this.trustThisBrowser(),
  }
  this.continueInProgress(true)
  Ajax.send(
    '%ModuleName%',
    'TrustDevice',
    parameters,
    function (response) {
      if (response && response.Result) {
        successCallback()
      } else {
        Api.showErrorByCode(response)
      }
    },
    this,
    null,
    authToken
  )
}

module.exports = new CVerifySecondFactorPopup()