import {
  linkClickSelector,
  buttonClickSelector,
  inputChangeSelector,
  formSubmitSelector,
  formInputClickSelector,
  formDisableSelector,
  formEnableSelector,
  fileInputSelector,
  linkDisableSelector,
  buttonDisableSelector
} from "./utils/constants"

import { ajax, href, isCrossDomain } from "./utils/ajax"
import { cspNonce, loadCSPNonce } from "./utils/csp"
import { csrfToken, csrfParam, CSRFProtection, refreshCSRFTokens } from "./utils/csrf"
import { matches, getData, setData, $ } from "./utils/dom"
import { fire, stopEverything, delegate } from "./utils/event"
import { serializeElement, formElements } from "./utils/form"

import { confirm, handleConfirmWithRails } from "./features/confirm"
import { handleDisabledElement, enableElement, disableElement } from "./features/disable"
import { handleMethodWithRails } from "./features/method"
import { handleRemoteWithRails, formSubmitButtonClick, preventInsignificantClick } from "./features/remote"

const Rails = {
  $,
  ajax,
  buttonClickSelector,
  buttonDisableSelector,
  confirm,
  cspNonce,
  csrfToken,
  csrfParam,
  CSRFProtection,
  delegate,
  disableElement,
  enableElement,
  fileInputSelector,
  fire,
  formElements,
  formEnableSelector,
  formDisableSelector,
  formInputClickSelector,
  formSubmitButtonClick,
  formSubmitSelector,
  getData,
  handleDisabledElement,
  href,
  inputChangeSelector,
  isCrossDomain,
  linkClickSelector,
  linkDisableSelector,
  loadCSPNonce,
  matches,
  preventInsignificantClick,
  refreshCSRFTokens,
  serializeElement,
  setData,
  stopEverything
}

// needs to be able to call Rails.confirm in case its overridden
const handleConfirm = handleConfirmWithRails(Rails)
Rails.handleConfirm = handleConfirm

// needs to be able to call Rails.href in case its overridden
const handleMethod = handleMethodWithRails(Rails)
Rails.handleMethod = handleMethod

// needs to be able to call Rails.href in case its overridden
const handleRemote = handleRemoteWithRails(Rails)
Rails.handleRemote = handleRemote

const start = function() {
  // Cut down on the number of issues from people inadvertently including
  // rails-ujs twice by detecting and raising an error when it happens.
  if (window._rails_loaded) { throw new Error("rails-ujs has already been loaded!") }

  // This event works the same as the load event, except that it fires every
  // time the page is loaded.
  // See https://github.com/rails/jquery-ujs/issues/357
  // See https://developer.mozilla.org/en-US/docs/Using_Firefox_1.5_caching
  window.addEventListener("pageshow", function() {
    $(formEnableSelector).forEach(function(el) {
      if (getData(el, "ujs:disabled")) {
        enableElement(el)
      }
    })
    $(linkDisableSelector).forEach(function(el) {
      if (getData(el, "ujs:disabled")) {
        enableElement(el)
      }
    })
  })

  delegate(document, linkDisableSelector, "ajax:complete", enableElement)
  delegate(document, linkDisableSelector, "ajax:stopped", enableElement)
  delegate(document, buttonDisableSelector, "ajax:complete", enableElement)
  delegate(document, buttonDisableSelector, "ajax:stopped", enableElement)

  delegate(document, linkClickSelector, "click", preventInsignificantClick)
  delegate(document, linkClickSelector, "click", handleDisabledElement)
  delegate(document, linkClickSelector, "click", handleConfirm)
  delegate(document, linkClickSelector, "click", disableElement)
  delegate(document, linkClickSelector, "click", handleRemote)
  delegate(document, linkClickSelector, "click", handleMethod)

  delegate(document, buttonClickSelector, "click", preventInsignificantClick)
  delegate(document, buttonClickSelector, "click", handleDisabledElement)
  delegate(document, buttonClickSelector, "click", handleConfirm)
  delegate(document, buttonClickSelector, "click", disableElement)
  delegate(document, buttonClickSelector, "click", handleRemote)

  delegate(document, inputChangeSelector, "change", handleDisabledElement)
  delegate(document, inputChangeSelector, "change", handleConfirm)
  delegate(document, inputChangeSelector, "change", handleRemote)

  delegate(document, formSubmitSelector, "submit", handleDisabledElement)
  delegate(document, formSubmitSelector, "submit", handleConfirm)
  delegate(document, formSubmitSelector, "submit", handleRemote)
  // Normal mode submit
  // Slight timeout so that the submit button gets properly serialized
  delegate(document, formSubmitSelector, "submit", e => setTimeout((() => disableElement(e)), 13))
  delegate(document, formSubmitSelector, "ajax:send", disableElement)
  delegate(document, formSubmitSelector, "ajax:complete", enableElement)

  delegate(document, formInputClickSelector, "click", preventInsignificantClick)
  delegate(document, formInputClickSelector, "click", handleDisabledElement)
  delegate(document, formInputClickSelector, "click", handleConfirm)
  delegate(document, formInputClickSelector, "click", formSubmitButtonClick)

  document.addEventListener("DOMContentLoaded", refreshCSRFTokens)
  document.addEventListener("DOMContentLoaded", loadCSPNonce)
  return window._rails_loaded = true
}
Rails.start = start

// For backward compatibility
if (typeof jQuery !== "undefined" && jQuery && jQuery.ajax) {
  if (jQuery.rails) { throw new Error("If you load both jquery_ujs and rails-ujs, use rails-ujs only.") }
  jQuery.rails = Rails
  jQuery.ajaxPrefilter(function(options, originalOptions, xhr) {
    if (!options.crossDomain) { return CSRFProtection(xhr) }
  })
}

// This block is to maintain backwards compatibility with the existing
// difference between what happens in a bundler and what happens using a
// sprockets compiler. In the sprockets case, Rails.start() is called
// automatically, but it is not in the ESModule case.
if (__esm == false && typeof exports !== "object" && typeof module === "undefined") {
  // The coffeescript bundle would set this at the very top. The Rollup bundle
  // doesn't set this until the entire bundle has finished running, so we need
  // to make sure its set before firing the rails:attachBindings event for
  // backwards compatibility.
  window.Rails = Rails

  if (fire(document, "rails:attachBindings")) {
    start()
  }
}

export default Rails
