/**
 * Braintree client interface
 * @external "braintree.Client"
 * @see {@link https://braintree.github.io/braintree-web/current/Client.html|Braintree Client Docs}
**/

/**
 * Braintree paypal interface
 * @external "braintree.PayPal"
 * @see {@link https://braintree.github.io/braintree-web/current/PayPal.html|Braintree Paypal Docs}
**/

/**
 * Braintree paypal interface
 * @external "braintree.ApplePay"
 * @see {@link https://braintree.github.io/braintree-web/current/ApplePay.html|Braintree Apple Pay Docs}
**/

/**
 * Braintree dataCollector interface
 * @external "braintree.DataCollector"
 * @see {@link https://braintree.github.io/braintree-web/current/DataCollector.html|Braintree DataCollector Docs}
**/

/**
 * jQuery.Deferred interface
 *
 * We use this for our promises because ES6 promises are non standard, and because jquery 1/2
 * promises do not play nicely with them.
 * @external "jQuery.Deferred"
 * @see {@link https://api.jquery.com/category/deferred-object/|jQuery Deferred Documentation}
**/

/**
 * Represents a wrapper around the braintree js library.
 *
 * This class is responsible for fetching tokens from a solidus store and using them
 * to manage a braintree client. It takes a number of options as capabilities for the client
 * depending on if you want to use use the data collector or paypal.
 *
 * We use this class mostly to hide the token operations for users.
 *
 * After creating the class, a call should be made to initialize before using it.
 * @see initialize
 *
 * @constructor
 * @param {Object} config Initalization options for the client
 * @param {Boolean} config.useDataCollector Use data collector capabilities for the braintree client
 * @param {Boolean} config.usePaypal Use Paypal capabilities for the braintree client
 * @param {requestCallback} config.readyCallback A callback to be invoked when the client is ready to go.
 * @param {Number} config.paymentMethodId A number indicating a specific payment method to be preferrred.
 *
**/
SolidusBraintree.Client = function(config) {
  this.paymentMethodId = config.paymentMethodId;
  this.readyCallback = config.readyCallback;
  this.useDataCollector = config.useDataCollector;
  this.usePaypal = config.usePaypal;
  this.useApplepay = config.useApplepay;
  this.useVenmo = config.useVenmo;
  this.flow = config.flow;
  this.venmoNewTabSupported = config.newBrowserTabSupported
  this.useThreeDSecure = config.useThreeDSecure;

  this._braintreeInstance = null;
  this._dataCollectorInstance = null;
  this._paypalInstance = null;
  this._venmoInstance = null;
  this._threeDSecureInstance = null;
};

/**
 * Fetches a client token from the backend and initializes the braintree client.
 * @returns {external:"jQuery.Deferred"} Promise to be invoked after initialization is complete
**/
SolidusBraintree.Client.prototype.initialize = function() {
  var initializationPromise = this._fetchToken().
    then(this._createBraintreeInstance.bind(this));

  if (this.useDataCollector) {
    initializationPromise = initializationPromise.then(this._createDataCollector.bind(this));
  }

  if (this.usePaypal) {
    initializationPromise = initializationPromise.then(this._createPaypal.bind(this));
  }

  if (this.useApplepay) {
    initializationPromise = initializationPromise.then(this._createApplepay.bind(this));
  }

  if (this.useVenmo) {
    initializationPromise = initializationPromise.then(this._createVenmo.bind(this));
  }

  if (this.useThreeDSecure) {
    initializationPromise = initializationPromise.then(this._createThreeDSecure.bind(this));
  }

  return initializationPromise.then(this._invokeReadyCallback.bind(this));
};

/**
 * Returns the braintree client instance
 * @returns {external:"braintree.Client"} The braintree client that was initialized by this class
**/
SolidusBraintree.Client.prototype.getBraintreeInstance = function() {
  return this._braintreeInstance;
};

/**
 * Returns the braintree paypal instance
 * @returns {external:"braintree.PayPal"} The braintree paypal that was initialized by this class
**/
SolidusBraintree.Client.prototype.getPaypalInstance = function() {
  return this._paypalInstance;
};

/**
 * Returns the braintree Apple Pay instance
 * @returns {external:"braintree.ApplePay"} The Braintree Apple Pay that was initialized by this class
**/
SolidusBraintree.Client.prototype.getApplepayInstance = function() {
  return this._applepayInstance;
};

/**
 * Returns the braintree Venmo instance
 * @returns {external:"braintree.Venmo"} The Braintree Venmo that was initialized by this class
**/
SolidusBraintree.Client.prototype.getVenmoInstance = function() {
  return this._venmoInstance;
};

/**
 * Returns the braintree dataCollector instance
 * @returns {external:"braintree.DataCollector"} The braintree dataCollector that was initialized by this class
**/
SolidusBraintree.Client.prototype.getDataCollectorInstance = function() {
  return this._dataCollectorInstance;
};


SolidusBraintree.Client.prototype._fetchToken = function() {
  var payload = {
    dataType: 'json',
    type: 'POST',
    url: SolidusBraintree.config.paths.clientTokens,
    error: function(xhr) {
      console.error("Error fetching braintree token");
    }
  };

  if (this.paymentMethodId) {
    payload.data = {
      payment_method_id: this.paymentMethodId
    };
  }

  return Spree.ajax(payload);
};

SolidusBraintree.Client.prototype._createBraintreeInstance = function(tokenResponse) {
  this.paymentMethodId = tokenResponse.payment_method_id;

  return SolidusBraintree.PromiseShim.convertBraintreePromise(braintree.client.create, [{
    authorization: tokenResponse.client_token
  }]).then(function (clientInstance) {
    this._braintreeInstance = clientInstance;
    return clientInstance;
  }.bind(this));
};

SolidusBraintree.Client.prototype._invokeReadyCallback = function() {
  if(this.readyCallback) {
    this.readyCallback(this._braintreeInstance);
  }

  return this;
};

SolidusBraintree.Client.prototype._createDataCollector = function() {
  return SolidusBraintree.PromiseShim.convertBraintreePromise(braintree.dataCollector.create, [{
    client: this._braintreeInstance,
    paypal: !!this.usePaypal
  }]).then(function (dataCollectorInstance) {
    this._dataCollectorInstance = dataCollectorInstance;
    return dataCollectorInstance;
  }.bind(this));
};

SolidusBraintree.Client.prototype._createPaypal = function() {
  return SolidusBraintree.PromiseShim.convertBraintreePromise(braintree.paypalCheckout.create, [{
    client: this._braintreeInstance
  }]).then(function (paypalInstance) {
    this._paypalInstance = paypalInstance;
    return paypalInstance;
  }.bind(this), function(error) {
    console.error(error.name + ':', error.message);
  });
};

SolidusBraintree.Client.prototype._createApplepay = function() {
  return SolidusBraintree.PromiseShim.convertBraintreePromise(braintree.applePay.create, [{
    client: this._braintreeInstance
  }]).then(function (applePayInstance) {
    this._applepayInstance = applePayInstance;
    return applePayInstance;
  }.bind(this));
};

SolidusBraintree.Client.prototype._createVenmo = function() {
  return SolidusBraintree.PromiseShim.convertBraintreePromise(braintree.venmo.create, [{
    client: this._braintreeInstance,
    allowDesktop: true,
    paymentMethodUsage: this.flow === 'vault' ? 'multi_use' : 'single_use',
    allowNewBrowserTab: this.venmoNewTabSupported
  }]).then(function (venmoInstance) {
    // Verify browser support before proceeding.
    if (!venmoInstance.isBrowserSupported()) {
      console.log('Browser does not support Venmo');
      return;
    }

    this._venmoInstance = venmoInstance;
    return venmoInstance;
  }.bind(this));
};

SolidusBraintree.Client.prototype._createThreeDSecure = function() {
  return SolidusBraintree.PromiseShim.convertBraintreePromise(braintree.threeDSecure.create, [{
    client: this._braintreeInstance,
    version: 2
  }]).then(function (threeDSecureInstance) {
    this._threeDSecureInstance = threeDSecureInstance;
  }.bind(this), function(error) {
    console.log(error);
  });
};