(function ($) {
  $(document).ready(function() {
    //Trigger a click on stripe checkout automatically
    var done = false; //Prevent double submit (for some reason)
    if(!done) {
      $("button.stripe-button-el").trigger("click");
      done = true;
    }

    $('.mepr-signup-form, #mepr-stripe-payment-form').each(function () {
      new MeprStripeForm(this);
    });
  });

  /**
   * The MemberPress Stripe form class
   *
   * @constructor
   * @param {HTMLFormElement} form
   */
  function MeprStripeForm (form) {
    this.form = form;
    this.$form = $(form);
    this.isSpc = this.$form.hasClass('mepr-signup-form');
    this.paymentMethods = [];
    this.selectedPaymentMethod = null;
    this.submitting = false;

    this.initPaymentMethods();
    this.$form.on('submit', $.proxy(this.handleSubmit, this));
  }

  /**
   * Initialize Stripe elements
   */
  MeprStripeForm.prototype.initPaymentMethods = function () {
    var self = this;

    self.$form.find('.mepr-stripe-card-element').each(function () {
      var $cardElement = $(this),
        $cardErrors = $cardElement.closest('.mp-form-row').find('.mepr-stripe-card-errors'),
        stripe = Stripe($cardElement.data('stripe-public-key')),
        elements = stripe.elements(),
        card = elements.create('card', { style: MeprStripeGateway.style }),
        paymentMethodId = $cardElement.data('payment-method-id'),
        wrapperSelector = self.isSpc ? '.mepr-payment-method' : '.mp_payment_form_wrapper',
        $wrapper = $cardElement.closest(wrapperSelector);

      card.mount($cardElement[0]);

      card.addEventListener('change', function (event) {
        $cardErrors.text(event.error ? event.error.message : '');
      });

      self.paymentMethods.push({
        id: paymentMethodId,
        stripe: stripe,
        card: card,
        $cardErrors: $cardErrors,
        $wrapper: $wrapper,
        subscriptionId: null
      });
    });
  };

  /**
   * Handle the payment form submission
   *
   * @param {jQuery.Event} e
   */
  MeprStripeForm.prototype.handleSubmit = function (e) {
    var self = this;

    e.preventDefault();

    if (self.submitting) {
      return;
    }

    self.submitting = true;

    if (self.$form.find('.mepr-payment-methods-wrapper').is(':hidden')) {
      self.form.submit();
      return;
    }

    self.$form.find('.mepr-submit').prop('disabled', true);
    self.$form.find('.mepr-loading-gif').show();

    self.selectedPaymentMethod = self.getSelectedPaymentMethod();

    if (self.selectedPaymentMethod) {
      var cardData = {
        billing_details: self.getBillingDetails(self.selectedPaymentMethod)
      };

      self.selectedPaymentMethod.stripe.createPaymentMethod('card', self.selectedPaymentMethod.card, cardData).then(function (result) {
        if (result.error) {
          self.handlePaymentError(result.error.message);
        } else {
          self.confirmPayment({
            payment_method_id: result.paymentMethod.id
          });
        }
      });
    } else {
      self.form.submit();
    }
  };

  /**
   * Get the currently selected payment method data
   *
   * @return {object|null}
   */
  MeprStripeForm.prototype.getSelectedPaymentMethod = function () {
    if (this.isSpc) {
      var paymentMethodId = this.$form.find('input[name="mepr_payment_method"]:checked').val();

      for (var i = 0; i < this.paymentMethods.length; i++) {
        if (this.paymentMethods[i].id === paymentMethodId) {
          return this.paymentMethods[i];
        }
      }

      return null;
    } else {
      return this.paymentMethods.length ? this.paymentMethods[0] : null;
    }
  };

  /**
   * Returns the form fields in a pretty key/value hash
   *
   * @return {object}
   */
  MeprStripeForm.prototype.getFormData = function () {
    var formData = new FormData( this.$form.get(0) );
    return Array.from(formData.entries()).reduce(function(obj, item) {
      obj[item[0]] = item[1];
      return obj;
    }, {});
  };

  /**
   * Get the billing details object to pass to Stripe
   *
   * @param  {object} selectedPaymentMethod
   * @return {object}
   */
  MeprStripeForm.prototype.getBillingDetails = function (selectedPaymentMethod) {
    var self = this,
      name = selectedPaymentMethod.$wrapper.find('input[name="card-name"]').val(),
      keys = {
        line1: 'mepr-address-one',
        line2: 'mepr-address-two',
        city: 'mepr-address-city',
        country: 'mepr-address-country',
        state: 'mepr-address-state',
        postal_code: 'mepr-address-zip'
      },
      address = {},
      addressFieldsPresent = false,
      details = {
        address: {}
      };

    if (typeof name == 'string' && name.length) {
      details.name = name;
    }

    $.each(keys, function (key, value) {
      var $field = self.$form.find('input[name="' + value + '"], select[name="' + value + '"]');

      if ($field.length) {
        var val = $field.val();

        if (typeof val == 'string' && val.length) {
          address[key] = val;
        }

        addressFieldsPresent = true;
      }
    });

    if (addressFieldsPresent) {
      details.address = address;
    } else {
      $.each(keys, function (key, value) {
        var cardAddressKey = value.replace('mepr-', 'card-'),
          $field = selectedPaymentMethod.$wrapper.find('input[name="' + cardAddressKey + '"]');

        if ($field.length) {
          var val = $field.val();

          if (typeof val == 'string' && val.length) {
            details.address[key] = val;
          }
        }
      });
    }

    return details;
  };

  /**
   * Allow the form to be submitted again
   */
  MeprStripeForm.prototype.allowResubmission = function () {
    this.submitting = false;
    this.$form.find('.mepr-submit').prop('disabled', false);
    this.$form.find('.mepr-loading-gif').hide();
    this.$form.find('.mepr-form-has-errors').show();
    this.$form.find('.mepr-validation-error, .mepr-top-error').remove();
  };

  /**
   * Handle form validation errors
   *
   * @param {array} errors The validation errors array
   */
  MeprStripeForm.prototype.handleValidationErrors = function (errors) {
    // Allow the form to be submitted again
    this.allowResubmission();

    var topErrors = [];

    for (var key in errors) {
      if (errors.hasOwnProperty(key)) {
        var $field = this.$form.find('[name="' + key + '"]').first(),
          $label = $field.closest('.mp-form-row').find('.mp-form-label');

        if ($.isNumeric(key) || !$label.length) {
          topErrors.push(errors[key]);
        } else {
          $label.append($('<span class="mepr-validation-error">').html(errors[key]));
        }

        console.log(errors[key]);
      }
    }

    if (topErrors.length) {
      var $list = $('<ul>'),
        $wrap = $('<div class="mepr-top-error mepr_error">');

      for (var i = 0; i < topErrors.length; i++) {
        $list.append($('<li>').html(MeprStripeGateway.top_error.replace('%s', topErrors[i])));
      }

      $wrap.append($list).prependTo(this.$form);
    }
  };

  /**
   * Handle an error with the payment
   *
   * @param {string} error The error message to display
   */
  MeprStripeForm.prototype.handlePaymentError = function (error) {
    // Allow the form to be submitted again
    this.allowResubmission();

    // Inform the user if there was an error
    this.selectedPaymentMethod.$cardErrors.html(error);
    console.log(error);
  };

  /**
   * Handle the response from our Ajax endpoint
   *
   * @param {object} response
   * @param {string} textStatus
   * @param {object} jqXHR
   */
  MeprStripeForm.prototype.handleServerResponse = function (response, textStatus, jqXHR) {
    if (response === null || typeof response != 'object') {
      this.handlePaymentError(MeprStripeGateway.invalid_response_error);
      this.debugCheckoutError({
        status: jqXHR.status,
        status_text: jqXHR.statusText,
        response_text: jqXHR.responseText,
        text_status: textStatus,
        error_thrown: 'Response was null or not an object'
      });
    } else {
      if (response.transaction_id) {
        this.$form.find('input[name="mepr_transaction_id"]').val(response.transaction_id);
      }

      if (response.subscription_id) {
        this.selectedPaymentMethod.subscriptionId = response.subscription_id;
      }

      if (response.errors) {
        this.handleValidationErrors(response.errors);
      } else if (response.error) {
        this.handlePaymentError(response.error);
      } else if (response.requires_action) {
        this.handleAction(response);
      } else if (!this.$form.hasClass('mepr-payment-submitted')) {
        this.$form.addClass('mepr-payment-submitted');
        this.form.submit();
      }
    }
  };

  /**
   * Displays the card action dialog to the user, and confirms the payment if successful
   *
   * @param {object} response
   */
  MeprStripeForm.prototype.handleAction = function (response) {
    var self = this,
      stripe = this.selectedPaymentMethod.stripe,
      card = this.selectedPaymentMethod.card,
      data;

    if (response.action === 'confirmCardSetup') {
      data = {
        payment_method: {
          card: card,
          billing_details: self.getBillingDetails(this.selectedPaymentMethod)
        }
      };

      stripe.confirmCardSetup(response.client_secret, data).then(function (result) {
        if (result.error) {
          self.handlePaymentError(result.error.message);
        } else {
          self.confirmPayment();
        }
      });
    } else if (response.action === 'confirmCardPayment') {
      data = {
        payment_method: {
          card: card,
          billing_details: self.getBillingDetails(this.selectedPaymentMethod)
        }
      };

      stripe.confirmCardPayment(response.client_secret, data).then(function (result) {
        if (result.error) {
          self.handlePaymentError(result.error.message);
        } else {
          self.confirmPayment();
        }
      });
    } else {
      stripe.handleCardAction(response.client_secret).then(function (result) {
        if (result.error) {
          self.handlePaymentError(result.error.message);
        } else {
          self.confirmPayment({
            payment_intent_id: result.paymentIntent.id
          });
        }
      });
    }
  };

  /**
   * Confirm the payment with our Ajax endpoint
   *
   * @param {object} [extraData] Additional data to send with the request
   */
  MeprStripeForm.prototype.confirmPayment = function (extraData) {
    var self = this,
      data = self.getFormData();

    $.extend(data, extraData || {}, {
      action: 'mepr_stripe_confirm_payment',
      mepr_current_url: document.location.href
    });

    if (self.selectedPaymentMethod.subscriptionId) {
      data.subscription_id = self.selectedPaymentMethod.subscriptionId;
    }

    // We don't want to hit our routes for processing the signup or payment forms
    delete data.mepr_process_signup_form;
    delete data.mepr_process_payment_form;

    var formData = new FormData();
    for (let key in data) {
      formData.append(key, data[key]);
    }

    $.ajax({
      type: 'POST',
      url: MeprStripeGateway.ajax_url,
      data: formData,
      dataType: 'json',
      cache: false,
      processData: false,
      contentType: false,
      headers: {
        'cache-control': 'no-cache'
      }
    })
    .done($.proxy(self.handleServerResponse, self))
    .fail(function (jqXHR, textStatus, errorThrown) {
      if (jqXHR.status === 0) {
        // Don't send a debug email for errors with status 0
        self.handlePaymentError(MeprStripeGateway.error_please_try_again);
      } else {
        self.handlePaymentError(MeprStripeGateway.ajax_error);
        self.debugCheckoutError({
          status: jqXHR.status,
          status_text: jqXHR.statusText,
          response_text: jqXHR.responseText,
          text_status: textStatus,
          error_thrown: '' + errorThrown
        });
      }
    });
  };

  /**
   * Debug a critical checkout error
   *
   * @param {object} data
   */
  MeprStripeForm.prototype.debugCheckoutError = function (data) {
    data.transaction_id = this.$form.find('input[name="mepr_transaction_id"]').val();
    data.customer_email = this.$form.find('input[name="user_email"]').val();

    $.ajax({
      type: 'POST',
      url: MeprStripeGateway.ajax_url,
      dataType: 'json',
      data: {
        action: 'mepr_stripe_debug_checkout_error',
        data: JSON.stringify(data)
      }
    });
  };
})(jQuery);
