/**
 * @package     Joomla.Plugin
 * @subpackage  Multifactorauth.webauthn
 *
 * @copyright   (C) 2022 Open Source Matters, Inc. <https://www.joomla.org>
 * @license     GNU General Public License version 2 or later; see LICENSE.txt
 */

((Joomla, document) => {

  let authData = null;
  const arrayToBase64String = a => btoa(String.fromCharCode(...a));
  const base64url2base64 = input => {
    let output = input.replace(/-/g, '+').replace(/_/g, '/');
    const pad = output.length % 4;
    if (pad) {
      if (pad === 1) {
        throw new Error('InvalidLengthError: Input base64url string is the wrong length to determine padding');
      }
      output += new Array(5 - pad).join('=');
    }
    return output;
  };
  const displayError = message => {
    try {
      Joomla.renderMessages({
        error: message
      });
    } catch (e) {
      alert(message);
    }
  };
  const handleError = message => {
    try {
      document.getElementById('plg_multifactorauth_webauthn_validate_button').style.disabled = 'null';
    } catch (e) {
      // Do nothing
    }
    displayError(message);
  };
  const setUp = e => {
    e.preventDefault();

    // Make sure the browser supports Webauthn
    if (!('credentials' in navigator)) {
      displayError(Joomla.Text._('PLG_MULTIFACTORAUTH_WEBAUTHN_ERR_NOTAVAILABLE_HEAD'));
      return false;
    }
    const rawPKData = document.forms['com-users-method-edit'].querySelectorAll('input[name="pkRequest"]')[0].value;
    const publicKey = JSON.parse(atob(rawPKData));

    // Convert the public key information to a format usable by the browser's credentials manager
    publicKey.challenge = Uint8Array.from(window.atob(base64url2base64(publicKey.challenge)), c => c.charCodeAt(0));
    publicKey.user.id = Uint8Array.from(window.atob(publicKey.user.id), c => c.charCodeAt(0));
    if (publicKey.excludeCredentials) {
      publicKey.excludeCredentials = publicKey.excludeCredentials.map(data => {
        data.id = Uint8Array.from(window.atob(base64url2base64(data.id)), c => c.charCodeAt(0));
        return data;
      });
    }

    // Ask the browser to prompt the user for their authenticator
    navigator.credentials.create({
      publicKey
    }).then(data => {
      const publicKeyCredential = {
        id: data.id,
        type: data.type,
        rawId: arrayToBase64String(new Uint8Array(data.rawId)),
        response: {
          clientDataJSON: arrayToBase64String(new Uint8Array(data.response.clientDataJSON)),
          attestationObject: arrayToBase64String(new Uint8Array(data.response.attestationObject))
        }
      };

      // Store the WebAuthn reply
      document.getElementById('com-users-method-code').value = btoa(JSON.stringify(publicKeyCredential));

      // Submit the form
      document.forms['com-users-method-edit'].submit();
    }, error => {
      // An error occurred: timeout, request to provide the authenticator refused, hardware / software
      // error...
      handleError(error);
    });
    return false;
  };
  const validate = () => {
    // Make sure the browser supports Webauthn
    if (!('credentials' in navigator)) {
      displayError(Joomla.Text._('PLG_MULTIFACTORAUTH_WEBAUTHN_ERR_NOTAVAILABLE_HEAD'));
      return;
    }
    const publicKey = authData;
    if (!publicKey.challenge) {
      handleError(Joomla.Text._('PLG_MULTIFACTORAUTH_WEBAUTHN_ERR_NO_STORED_CREDENTIAL'));
      return;
    }
    publicKey.challenge = Uint8Array.from(window.atob(base64url2base64(publicKey.challenge)), c => c.charCodeAt(0));
    if (publicKey.allowCredentials) {
      publicKey.allowCredentials = publicKey.allowCredentials.map(data => {
        data.id = Uint8Array.from(window.atob(base64url2base64(data.id)), c => c.charCodeAt(0));
        return data;
      });
    }
    navigator.credentials.get({
      publicKey
    }).then(data => {
      const publicKeyCredential = {
        id: data.id,
        type: data.type,
        rawId: arrayToBase64String(new Uint8Array(data.rawId)),
        response: {
          authenticatorData: arrayToBase64String(new Uint8Array(data.response.authenticatorData)),
          clientDataJSON: arrayToBase64String(new Uint8Array(data.response.clientDataJSON)),
          signature: arrayToBase64String(new Uint8Array(data.response.signature)),
          userHandle: data.response.userHandle ? arrayToBase64String(new Uint8Array(data.response.userHandle)) : null
        }
      };
      document.getElementById('users-mfa-code').value = btoa(JSON.stringify(publicKeyCredential));
      document.getElementById('users-mfa-captive-form').submit();
    }, error => {
      // Example: timeout, interaction refused...
      handleError(error);
    });
  };
  const onValidateClick = event => {
    event.preventDefault();
    authData = JSON.parse(window.atob(Joomla.getOptions('com_users.authData')));
    document.getElementById('users-mfa-captive-button-submit').style.disabled = 'disabled';
    validate();
    return false;
  };
  document.getElementById('multifactorauth-webauthn-missing').style.display = 'none';
  if (typeof navigator.credentials === 'undefined') {
    document.getElementById('multifactorauth-webauthn-missing').style.display = 'block';
    document.getElementById('multifactorauth-webauthn-controls').style.display = 'none';
  }
  window.addEventListener('DOMContentLoaded', () => {
    if (Joomla.getOptions('com_users.pagetype') === 'validate') {
      document.getElementById('users-mfa-captive-button-submit').addEventListener('click', onValidateClick);
    } else {
      document.querySelectorAll('.multifactorauth_webauthn_setup').forEach(btn => btn.addEventListener('click', setUp));
    }
  });
})(Joomla, document);
