'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.redirectToSignInWithAuthRequestImpl = redirectToSignInWithAuthRequestImpl;
exports.redirectToSignInImpl = redirectToSignInImpl;
exports.handlePendingSignInImpl = handlePendingSignInImpl;
exports.loadUserDataImpl = loadUserDataImpl;

var _jsontokens = require('jsontokens');

var _customProtocolDetectionBlockstack = require('custom-protocol-detection-blockstack');

var _customProtocolDetectionBlockstack2 = _interopRequireDefault(_customProtocolDetectionBlockstack);

var _index = require('./index');

var _utils = require('../utils');

var _index2 = require('../index');

var _errors = require('../errors');

var _authMessages = require('./authMessages');

var _authConstants = require('./authConstants');

var _profiles = require('../profiles');

var _logger = require('../logger');

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

var DEFAULT_PROFILE = {
  '@type': 'Person',
  '@context': 'http://schema.org'

  /**
   * Redirects the user to the Blockstack browser to approve the sign in request
   * given.
   *
   * The user is redirected to the `blockstackIDHost` if the `blockstack:`
   * protocol handler is not detected. Please note that the protocol handler detection
   * does not work on all browsers.
   * @param  {UserSession} caller - the instance calling this method
   * @param  {String} authRequest - the authentication request generated by `makeAuthRequest`
   * @param  {String} blockstackIDHost - the URL to redirect the user to if the blockstack
   *                                     protocol handler is not detected
   * @return {void}
   * @private
   */
};function redirectToSignInWithAuthRequestImpl(caller, authRequest) {
  var protocolURI = _utils.BLOCKSTACK_HANDLER + ':' + authRequest;

  var httpsURI = _authConstants.DEFAULT_BLOCKSTACK_HOST + '?authRequest=' + authRequest;

  if (caller.appConfig && caller.appConfig.authenticatorURL) {
    httpsURI = caller.appConfig.authenticatorURL + '?authRequest=' + authRequest;
  }

  // If they're on a mobile OS, always redirect them to HTTPS site
  if (/Android|webOS|iPhone|iPad|iPod|Opera Mini/i.test(navigator.userAgent)) {
    _logger.Logger.info('detected mobile OS, sending to https');
    window.location = httpsURI;
    return;
  }

  function successCallback() {
    _logger.Logger.info('protocol handler detected');
    // protocolCheck should open the link for us
  }

  function failCallback() {
    _logger.Logger.warn('protocol handler not detected');
    window.location = httpsURI;
  }

  function unsupportedBrowserCallback() {
    // Safari is unsupported by protocolCheck
    _logger.Logger.warn('can not detect custom protocols on this browser');
    window.location = protocolURI;
  }

  (0, _customProtocolDetectionBlockstack2.default)(protocolURI, failCallback, successCallback, unsupportedBrowserCallback);
}

/**
 * Generates an authentication request and redirects the user to the Blockstack
 * browser to approve the sign in request.
 *
 * Please note that this requires that the web browser properly handles the
 * `blockstack:` URL protocol handler.
 *
 * Most web applications should use this
 * method for sign in unless they require more fine grained control over how the
 * authentication request is generated. If your app falls into this category,
 * use `makeAuthRequest`,
 * and `redirectToSignInWithAuthRequest` to build your own sign in process.
 * @param {UserSession} caller - the instance calling this function
 * @return {void}
 * @private
 */
function redirectToSignInImpl(caller) {
  var transitKey = caller.generateAndStoreTransitKey();
  var authRequest = caller.makeAuthRequest(transitKey);
  redirectToSignInWithAuthRequestImpl(caller, authRequest);
}

/**
 * Try to process any pending sign in request by returning a `Promise` that resolves
 * to the user data object if the sign in succeeds.
 *
 * @param {UserSession} caller - the instance calling this function
 * @param {String} authResponseToken - the signed authentication response token
 * @return {Promise} that resolves to the user data object if successful and rejects
 * if handling the sign in request fails or there was no pending sign in request.
 * @private
 */
function handlePendingSignInImpl(caller, authResponseToken) {
  var transitKey = caller.store.getSessionData().transitKey;

  var coreNode = _authConstants.DEFAULT_CORE_NODE;

  var coreNodeSessionValue = caller.store.getSessionData().coreNode;
  if (coreNodeSessionValue) {
    coreNode = coreNodeSessionValue;
  }

  var nameLookupURL = '' + coreNode + _authConstants.NAME_LOOKUP_PATH;

  return (0, _index.verifyAuthResponse)(authResponseToken, nameLookupURL).then(function (isValid) {
    if (!isValid) {
      throw new _errors.LoginFailedError('Invalid authentication response.');
    }
    var tokenPayload = (0, _jsontokens.decodeToken)(authResponseToken).payload;
    // TODO: real version handling
    var appPrivateKey = tokenPayload.private_key;
    var coreSessionToken = tokenPayload.core_token;
    if ((0, _utils.isLaterVersion)(tokenPayload.version, '1.1.0')) {
      if (transitKey !== undefined && transitKey != null) {
        if (tokenPayload.private_key !== undefined && tokenPayload.private_key !== null) {
          try {
            appPrivateKey = (0, _authMessages.decryptPrivateKey)(transitKey, tokenPayload.private_key);
          } catch (e) {
            _logger.Logger.warn('Failed decryption of appPrivateKey, will try to use as given');
            try {
              (0, _utils.hexStringToECPair)(tokenPayload.private_key);
            } catch (ecPairError) {
              throw new _errors.LoginFailedError('Failed decrypting appPrivateKey. Usually means' + ' that the transit key has changed during login.');
            }
          }
        }
        if (coreSessionToken !== undefined && coreSessionToken !== null) {
          try {
            coreSessionToken = (0, _authMessages.decryptPrivateKey)(transitKey, coreSessionToken);
          } catch (e) {
            _logger.Logger.info('Failed decryption of coreSessionToken, will try to use as given');
          }
        }
      } else {
        throw new _errors.LoginFailedError('Authenticating with protocol > 1.1.0 requires transit' + ' key, and none found.');
      }
    }
    var hubUrl = _authConstants.BLOCKSTACK_DEFAULT_GAIA_HUB_URL;
    if ((0, _utils.isLaterVersion)(tokenPayload.version, '1.2.0') && tokenPayload.hubUrl !== null && tokenPayload.hubUrl !== undefined) {
      hubUrl = tokenPayload.hubUrl;
    }

    var userData = {
      username: tokenPayload.username,
      profile: tokenPayload.profile,
      decentralizedID: tokenPayload.iss,
      identityAddress: (0, _index2.getAddressFromDID)(tokenPayload.iss),
      appPrivateKey: appPrivateKey,
      coreSessionToken: coreSessionToken,
      authResponseToken: authResponseToken,
      hubUrl: hubUrl
    };
    var profileURL = tokenPayload.profile_url;
    if ((userData.profile === null || userData.profile === undefined) && profileURL !== undefined && profileURL !== null) {
      return fetch(profileURL).then(function (response) {
        if (!response.ok) {
          // return blank profile if we fail to fetch
          userData.profile = Object.assign({}, DEFAULT_PROFILE);
          var sessionData = caller.store.getSessionData();
          sessionData.userData = userData;
          caller.store.setSessionData(sessionData);
          return userData;
        } else {
          return response.text().then(function (responseText) {
            return JSON.parse(responseText);
          }).then(function (wrappedProfile) {
            return (0, _profiles.extractProfile)(wrappedProfile[0].token);
          }).then(function (profile) {
            var sessionData = caller.store.getSessionData();
            userData.profile = profile;
            sessionData.userData = userData;
            caller.store.setSessionData(sessionData);
            return userData;
          });
        }
      });
    } else {
      var sessionData = caller.store.getSessionData();
      userData.profile = tokenPayload.profile;
      sessionData.userData = userData;
      caller.store.setSessionData(sessionData);
      return userData;
    }
  });
}

/**
 * Retrieves the user data object. The user's profile is stored in the key `profile`.
 *
 *  @param {UserSession} caller - the instance calling this function
 *  @return {Object} User data object.
 *  @private
 */
function loadUserDataImpl(caller) {
  var userData = caller.store.getSessionData().userData;
  if (!userData) {
    throw new _errors.InvalidStateError('No user data found. Did the user sign in?');
  }
  return userData;
}