import { initLocations, filterStates } from "./modules/locations.min.js";
import {
  stripe,
  stripeInit,
  stripeUpdate,
  stripeSubmit,
  stripeResults,
  haveStripeClientSecret,
} from "./modules/stripe.min.js";
import {
  paypalInit,
  paypalDonationSet,
  setupPaypalOneTimeButtons,
  setupPaypalSubscriptionButtons,
  paypalResults,
} from "./modules/paypal.min.js";

/* namespace code dependent on DOM */
var dTor = dTor || {};

dTor.baseUrl = window.location.origin;
dTor.page = $("body").attr("id") || "";
dTor.csrfToken = $("[name=csrfmiddlewaretoken]").val();
dTor.minimumDonation = document.getElementById('minimum-donation-data')
  ? JSON.parse(document.getElementById('minimum-donation-data').textContent)
  : {};

/*
 * function: init
 * purpose:
 *    perform DOM manipulation and
 *    bind interactions after
 *    DOM loads
 * attrs: n/a
 */

dTor.init = function () {
  // Initialize country and state location data
  // 233 is the magic ID number for USA
  initLocations();

  this.refreshCaptcha = function () {
    const widget = document.querySelector('altcha-widget');
    if (typeof widget.reset === "function") {
      widget.reset();
    }
  };

  /*
   * function: displayPaymentError
   * purpose:
   *    handle errors encountered
   *
   */
  this.displayPaymentError = function (message) {
    $("form.was-validated").removeClass("was-validated");
    dTor.refreshCaptcha();
    const messageContainer = $("#result-message");
    messageContainer.text(message).removeClass("hidden");
  };

  this.hidePaymentError = function() {
    const messageContainer = $("#result-message");
    messageContainer.text("").addClass("hidden");
  }

  this.handleFormErrors = function (errors) {
    $("form.was-validated").removeClass("was-validated");
    dTor.refreshCaptcha();
    for (const [key, value] of Object.entries(errors)) {
      switch (key) {
        case "altcha":
          const widget = document.querySelector('altcha-widget');
              widget.setState("error", value);
          document.getElementById("btn-donateSubmit").disabled = true;
          break;
        default:
          let el = $(`[name='${key}']`);
          if (el.length) {
            $(`[name='${key}']`).addClass("is-invalid");
          } else {
            console.log(`Unexpected form error with key '${key}': ${value}`);
          }
          break;
      }
    }
  };


  /*
   * function: setupFormValidation
   * purpose:
   *    set up donation form
   *    validation for common
   *    issues before it gets to
   *    backend.
   * attrs: n/a
   */
  this.setupFormValidation = function () {
    dTor.hidePaymentError();
    $("form").on("submit", function (e) {
      if (
        !this.checkValidity() ||
        $(e.currentTarget).data("shippingRestricted")
      ) {
        e.preventDefault();
        e.stopPropagation();
        $.fn.matchHeight._update();
        if($(e.currentTarget).data("shippingRestricted")) {
          dTor.displayPaymentError("Due to shipping restrictions, we currently can not ship to Ukraine, or Russia. We apologize for the inconvenience.");
        }
      } else {
        dTor.beginPaymentProcess(e);
      }

      $(this).addClass("was-validated");
    });
  };


  this.validateElement = function (el, setValidationMessage = true) {
    el.setCustomValidity("");
    if (!el.checkValidity() || $(el).val() == null) {
      if (setValidationMessage) {
          $(el).siblings(".invalid-feedback").text(el.validationMessage);
      }
      $(el).addClass("is-invalid");
    } else {
      $(el).removeClass("is-invalid");
    }
  };


  this.formSyncInput = function (e) {
    let _this = $(this);
    let thisId = _this.attr("id");
    let thisType = _this.attr("type");
    if (thisType == "button" || thisId.startsWith("altcha_checkbox")) return;

    if (_this.attr("required")) {
      dTor.validateElement(this);
    }
  };


  this.formSyncSelect = function (e) {
    let _this = $(this);
    let _thisLabel = $(this).siblings("label");

    if (_this.attr("required") || _thisLabel.hasClass("required")) {
      dTor.validateElement(this);
    }
  };


  /*
   * function: propagateDonation
   * purpose:
   *    when donation value changes,
   *    propagate implications of new value
   *    across form
   * attrs:
   *    d: donation value (in cents)
   */
  this.propagateDonation = function (d) {
    var dShow = "$" + d / 100;
    var frequency = $("#pricegrid").data("frequency") || "single";

    /* update Stripe payment element */
      if (parseInt(d) >= dTor.minimumDonation.cents) {
        stripeUpdate(d, frequency);
      };

    /* update paypal order */
    if (parseInt(d) >= dTor.minimumDonation.cents) paypalDonationSet(d);

    /* ensure valid, and only valid, perks are selectable and selected */
    if ($("#noPerkCheckbox").length) {
      $(".Perk-selection").each(function (i, el) {
        var _el = $(el);
        if (_el.data("price-tier-" + frequency) <= d) {
          _el.find("input[type='radio']").removeAttr("disabled");
          _el.removeClass("Perk-inactive").addClass("Perk-active");
        } else {
          _el
            .find("[id^='id_perk']")
            .prop("checked", false)
            .attr("disabled", "disabled");
          _el.removeClass("Perk-active").addClass("Perk-inactive");
        }
      });
      /* validate perk radio element which may now be blank */
      dTor.validateElement($("input[name='perk']")[0], false);
    }

    /* Customize BTCPay submit button copy */
    if ($("#cryptoContainer").length) {
      if (!$("#secBTCPay").hasClass("hidden")) {
        var buttonCopy = $("#btn-donateBTCPay")[0];
        buttonCopy.innerHTML = "Donate <span>" + dShow + "</span> by BTCPay";
      }
    }
  };

  /* toggle perk option visibility given relevant perk ID; */
  /* handle hidden form fields related to no-option perks */
  this.togglePerks = function (perkId) {
    $(".perk_option").each(function (i, el) {
      let _el = $(el);
      if (perkId == _el.data("perk")) {
        _el.removeClass("hidden");
        _el.find("select").attr("required", "required");
        _el.find(".hiddenPerkOption").removeAttr("disabled");
      } else {
        if (!_el.hasClass("hidden")) _el.addClass("hidden");
        _el.find("select").removeAttr("required");
        _el.find(".hiddenPerkOption").attr("disabled", "disabled");
      }
    });
    /* validate perk radio element validity */
    dTor.validateElement($("input[name='perk']")[0], false);
  };

  this.beginPaymentProcess = function(e) {
    e.preventDefault();
    if(!$("#checkout_card").hasClass("hidden")) {
      stripeSubmit().then(function (errors) {
        if (errors) {
          dTor.handleFormErrors(errors);
        }
      });
    }
  }

  /* * * * * * * * * * * *
   * Interaction binding *
   * * * * * * * * * * * */

  /* per-field form validation */
  $(".donate-form, #subscriptionForm")
  .on("change keyup", "input", this.formSyncInput)
  .on("change keyup blur", "select", this.formSyncSelect);


  /* Section toggle switches
     (e.g., Paypal vs Stripe forms; crypto QR codes vs BTCPay)
  */
  $(".sectionToggle").on("click", "[data-toggles]", function (e) {
    e.preventDefault();
    var _this = $(this);
    _this.addClass("active").siblings("[data-toggles]").removeClass("active");
    var targetID = _this.data("toggles");
    $(".toggleable").each(function () {
      if ($(this).is(targetID)) {
        $(this).removeClass("hidden");
      } else {
        $(this).addClass("hidden");
      }
    });
    /* ensure visible elements have been matchHeighted */
    $.fn.matchHeight._update();
  });

  /* Price grid donation frequency toggle switches
     (also manages default selection)
  */
  $(".frequencyToggle")
    .on("click", "[data-toggles_price]", function (e) {
      // e.preventDefault();
      var _this = $(this);
      var targetID = _this.data("toggles_price");
      var frequency = _this.data("toggles_to");
      var currentVal = $("#customDonation").val() * 100;

      /* manipulate radio button group visbility;
       maintain donation selection between categories */
      $(".Perk").removeClass("single monthly").addClass(frequency);
      $("#pricegrid")
        .data("frequency", frequency)
        .find(":checked")
        .removeAttr("checked");
      $(".toggleablePrice").each(function () {
        if ($(this).is(targetID)) {
          let checkableChildren = $(this).find(
            "input[value='" + currentVal + "']"
          );
          if (checkableChildren) {
            checkableChildren.prop("checked", true);
          }
          $(this).removeClass("hidden");
        } else {
          $(this).addClass("hidden");
        }
      });

      $("#checkout_paypal").removeClass("single monthly").addClass(frequency);

      /* ensure relevant fields update */
      dTor.propagateDonation(currentVal);

      /* ensure visible elements have been matchHeighted */
      $.fn.matchHeight._update();
    })
    .on("blur", "input[id^='customDonation']", function (e) {
      var _this = $(this);
      dTor.propagateDonation(_this[0].value * 100);
    });

  /* Sync donation input field with radio button values */
  $("[id^='pricegrid_']").on("click", "input[type='radio']", function (e) {
    var _this = $(this);
    var _parent = $("#pricegrid");
    var _customInput = _parent.find("input[id^='customDonation']");
    var donationAmount = _this[0].value;
    var donationDisplay = donationAmount / 100;

    _customInput[0].value = donationDisplay;
    dTor.validateElement(_customInput[0]);

    dTor.propagateDonation(donationAmount);
  });

  /* Sync radio button values with donation input field */
  $("#pricegrid").on("change, keyup", "#customDonation", function (e) {
    var _this = $(this);
    var _parent = $("#pricegrid");
    var priceGroup = _parent.find(".toggleablePrice").not(".hidden");

    priceGroup.find("input[type='radio']").each(function (i, e) {
      var _e = $(e);
      if (_this[0].value * 100 == _e.val()) {
        _e.prop("checked", true);
        _e.attr("checked", "checked");
      } else {
        _e.prop("checked", false);
      }
    });
    /* ensure visible elements have been matchHeighted */
    $.fn.matchHeight._update();

    dTor.propagateDonation(_this[0].value * 100);
  });

  $(".Card-gift")
    /* Toggle perk_options field */
    .on("click", "input[type='radio']", function (e) {
      var _this = $(this);
      if (_this.prop("checked")) {
        dTor.togglePerks(_this.val());
      }
    });

  /* Refresh "state" form field options when selecting a country */
  $("form").on("change keyup blur", "#id_country", function (e) {
    var cName = $(e.currentTarget).val(),
      cOption = $(e.currentTarget).find('option[value="' + cName + '"]'),
      cId = cOption.data("countryid"),
      parentForm = $(e.currentTarget).closest("form");

    /* refresh location  */
    filterStates(cOption.data("countryid"));

    /* Disable form for restricted countries */
    if (cId == 182 || cId == 230) {
      parentForm.find(".shippingRestricted").removeClass("hidden");
      parentForm.data("shippingRestricted", true);
    } else {
      if (!parentForm.find(".shippingRestricted").hasClass("hidden")) {
        parentForm.find(".shippingRestricted").addClass("hidden");
      }
      parentForm.data("shippingRestricted", false);
    }
  });

  /* bind listener to option group containers to toggle option lists based on option group choice */
  $(".perk_option").on("change keyup blur", "select.optionGroup", function (e) {
    // ensure all related option lists are re-hidden
    $(this).closest(".perk_option").find(".optionList")
      .removeAttr("required")
      .filter(":not(.hidden)")
      .addClass("hidden")
      .val("");
    // reveal specific option list
    $("#perk_option--" + $(e.currentTarget).val()).removeClass("hidden").attr("required", "required");
    // reveal option list container label
    $("label[data-revealed-by='optionGroup-" + $(e.currentTarget).data("selectid") + "']").removeClass("hidden");
  });

  /* bootstrap5 validation for btcpay form */
  $("form#cryptoBTCPay").on("submit", function (e) {
    if ( !this.checkValidity() ) {
      e.preventDefault();
      e.stopPropagation();
    }
    $(this).addClass("was-validated");
  })
  .on("change keyup", "input", function(e) {
    // keep hidden field used by BTCPay synced with user-accessible field
    $("#price").val($("#customDonation").val());
    dTor.validateElement(this);
  });

  /* click-to-copy button on crypto fields */
  $("#secCryptoAddresses")
    .on("click", ".currencyAddress--copy", function (e) {
      e.preventDefault();
      var _this = $(this);
      var address = _this.siblings(".currencyAddress").attr("value");
      navigator.clipboard.writeText(address);
      let $icon = _this.find("i");
      $icon.removeClass("icon-copy").addClass("icon-check");
    });

  /*
  This sets the custom validation message issued by the browser, which can be
  displayed if a user attempts to manually set the field value below the minimum
  donation amount.
  */
  $("#customDonation").on("invalid", function (e) {
    $(this).get(0).setCustomValidity(`Please enter a donation of at least ${dTor.minimumDonation.display}.`);
  });

  /*
  In that occurrence, this will clear the aforementioned error message when the user
  goes back to amend it.
  */
  $("#customDonation").on("input", function (e) {
    $(this).get(0).setCustomValidity("");
  });

  switch (dTor.page) {
    case "donate":
      /* instantiate Paypal buttons + scripting */
      setupPaypalOneTimeButtons();
      setupPaypalSubscriptionButtons();

      /* instantiate stripe object + DOM elems */
      stripeInit(12500, dTor.csrfToken, dTor.baseUrl, dTor.displayPaymentError, dTor.handleFormErrors);

      /* instantiate paypal buttons */
      paypalInit(12500, dTor.csrfToken, dTor.baseUrl, dTor.displayPaymentError, dTor.handleFormErrors);

      /* instantiate form validation */
      dTor.setupFormValidation();

      /* Instantiate after pageload with default selection */
      dTor.propagateDonation(12500);

      break;
    case "thankyou":
      stripeResults();
      paypalResults();
      break;
    case "crypto":
      break;
    default:
      break;
  }
};

window.onload = dTor.init();
