(function ($) {
  "use strict";

  var observer = null;
  var INTERACTIVE_SELECTOR = "button, input, a.button, a[href*='action='], .components-button, [role='button']";

  function getData() {
    return window.polangerShieldDemoLockRuntime || {};
  }

  function getTransport() {
    return getData().transport || {};
  }

  function i18n(key, fallback) {
    var data = getData();
    if (data.i18n && data.i18n[key]) {
      return data.i18n[key];
    }
    return fallback;
  }

  function showToast(message, isError) {
    var toast = $('<div class="polanger-shield-toast" />').text(message || "");
    if (isError) {
      toast.addClass("is-error");
    }

    $("body").append(toast);
    setTimeout(function () {
      toast.addClass("is-visible");
    }, 10);

    setTimeout(function () {
      toast.removeClass("is-visible");
      setTimeout(function () {
        toast.remove();
      }, 220);
    }, 2400);
  }

  function normalizeText(value) {
    return String(value || "").toLowerCase().replace(/\s+/g, " ").trim();
  }

  function getUrlAction(url) {
    if (!url) {
      return "";
    }

    try {
      var resolvedUrl = new URL(url, window.location.href);
      return normalizeText(resolvedUrl.searchParams.get("action") || resolvedUrl.searchParams.get("action2") || "");
    } catch (error) {
      return "";
    }
  }

  function looksMutatingAction(action, allowEmpty) {
    action = normalizeText(action);

    if (!action) {
      return !!allowEmpty;
    }

    if (action === "heartbeat") {
      return false;
    }

    var tokens = [
      "save",
      "update",
      "publish",
      "submit",
      "delete",
      "remove",
      "activate",
      "deactivate",
      "install",
      "import",
      "export",
      "reset",
      "trash",
      "create",
      "insert",
      "duplicate",
      "inline-save"
    ];

    for (var i = 0; i < tokens.length; i++) {
      if (action.indexOf(tokens[i]) !== -1) {
        return true;
      }
    }

    return false;
  }

  function getBannerStorageKey() {
    return "polangerShieldDemoLockBannerDismissed:" + String(getData().ruleId || 0);
  }

  function isBannerCollapsed() {
    try {
      return window.localStorage.getItem(getBannerStorageKey()) === "1";
    } catch (error) {
      return false;
    }
  }

  function persistBannerCollapsed(collapsed) {
    try {
      if (collapsed) {
        window.localStorage.setItem(getBannerStorageKey(), "1");
        return;
      }

      window.localStorage.removeItem(getBannerStorageKey());
    } catch (error) {
      // Ignore storage failures so the notice still works in restricted browsers.
    }
  }

  function setBannerCollapsed(collapsed) {
    var notice = document.getElementById("polanger-shield-demo-lock-notice");
    if (!notice) {
      return;
    }

    var banner = document.getElementById("polanger-shield-demo-lock-banner");
    var toggle = notice.querySelector(".polanger-shield-demo-lock-banner-toggle");

    notice.classList.toggle("is-collapsed", !!collapsed);

    if (banner) {
      banner.hidden = !!collapsed;
    }

    if (toggle) {
      toggle.hidden = !collapsed;
      toggle.setAttribute("aria-expanded", collapsed ? "false" : "true");
    }
  }

  function createBanner() {
    if (!getData().showNotice || document.getElementById("polanger-shield-demo-lock-notice")) {
      return;
    }

    var collapsed = isBannerCollapsed();
    var title = $("<div>").text(i18n("bannerTitle", "Demo Lock Active")).html();
    var text = $("<div>").text(i18n("bannerText", "You can review this admin screen, but save buttons, form submissions, and write requests are disabled for your account.")).html();
    var dismiss = $("<div>").text(i18n("dismissLabel", "Understood")).html();

    var banner =
      '<div id="polanger-shield-demo-lock-notice" class="polanger-shield-demo-lock-notice' + (collapsed ? " is-collapsed" : "") + '">' +
        '<button type="button" class="polanger-shield-demo-lock-banner-toggle" aria-expanded="' + (collapsed ? "false" : "true") + '" title="' + title + '" aria-label="' + title + '"' + (collapsed ? "" : " hidden") + ">" +
          '<span class="dashicons dashicons-info-outline" aria-hidden="true"></span>' +
        "</button>" +
        '<div id="polanger-shield-demo-lock-banner" class="polanger-shield-demo-lock-banner"' + (collapsed ? " hidden" : "") + ">" +
          '<div class="polanger-shield-demo-lock-banner__icon" aria-hidden="true">' +
            '<span class="dashicons dashicons-lock"></span>' +
          "</div>" +
          '<div class="polanger-shield-demo-lock-banner__copy">' +
            "<strong>" + title + "</strong>" +
            "<p>" + text + "</p>" +
          "</div>" +
          '<button type="button" class="polanger-shield-demo-lock-banner__dismiss">' + dismiss + "</button>" +
        "</div>" +
      "</div>";

    var $container = $("#wpbody-content");
    if (!$container.length) {
      $container = $("#wpcontent");
    }
    if (!$container.length) {
      $container = $("body");
    }

    $container.prepend(banner);
  }

  function isMutatingForm(form) {
    if (!form || form.tagName !== "FORM") {
      return false;
    }

    var method = normalizeText(form.getAttribute("method") || form.method || "get");
    return method !== "get";
  }

  function getElementSignature(el) {
    if (!el) {
      return "";
    }

    return normalizeText([
      el.id || "",
      el.name || "",
      el.value || "",
      el.getAttribute("aria-label") || "",
      el.getAttribute("title") || "",
      el.className || "",
      $(el).text() || ""
    ].join(" "));
  }

  function isLikelyMutatingControl(el) {
    if (!el || !document.body.contains(el)) {
      return false;
    }

    if ($(el).closest("#polanger-shield-trigger, #polanger-shield-panel, #polanger-shield-demo-lock-notice, .polanger-shield-modal-wrap").length) {
      return false;
    }

    var $el = $(el);
    if ($el.is("[data-polanger-demo-lock='1']")) {
      return true;
    }

    var hrefAction = $el.is("a") ? getUrlAction($el.attr("href")) : "";
    if (hrefAction && looksMutatingAction(hrefAction, false)) {
      return true;
    }

    if ($el.is("button[type='submit'], input[type='submit'], #publish, #save-post, #submit, .editor-post-publish-button, .editor-post-save-draft, .editor-post-publish-panel__toggle, .woocommerce-save-button")) {
      return true;
    }

    var signature = getElementSignature(el);
    var tokens = [
      "save",
      "update",
      "publish",
      "submit",
      "delete",
      "remove",
      "activate",
      "deactivate",
      "install",
      "import",
      "export",
      "reset",
      "trash"
    ];

    if ($el.is("button, input[type='button'], input[type='submit'], a.button, .components-button, [role='button']")) {
      for (var i = 0; i < tokens.length; i++) {
        if (signature.indexOf(tokens[i]) !== -1) {
          return true;
        }
      }
    }

    return !!$el.closest("form").length && isMutatingForm($el.closest("form")[0]) && $el.is("button, input[type='button'], input[type='submit'], a.button");
  }

  function decorateControl(el) {
    if (!el || !getData().disableControls) {
      return;
    }

    if (!isLikelyMutatingControl(el)) {
      return;
    }

    el.setAttribute("data-polanger-demo-lock", "1");
    el.removeAttribute("data-polanger-demo-lock-label");
    el.setAttribute("aria-disabled", "true");
    el.classList.add("polanger-shield-demo-lock-control");
  }

  function decorateControls() {
    if (!getData().disableControls) {
      return;
    }

    document.querySelectorAll(INTERACTIVE_SELECTOR).forEach(decorateControl);
  }

  function ensureFormTransportFields(form) {
    if (!form || !isMutatingForm(form)) {
      return;
    }

    var transport = getTransport();
    if (!transport.ruleField || !transport.tokenField) {
      return;
    }

    var ruleField = form.querySelector('input[name="' + transport.ruleField + '"]');
    if (!ruleField) {
      ruleField = document.createElement("input");
      ruleField.type = "hidden";
      ruleField.name = transport.ruleField;
      form.appendChild(ruleField);
    }
    ruleField.value = transport.ruleId || "";

    var tokenField = form.querySelector('input[name="' + transport.tokenField + '"]');
    if (!tokenField) {
      tokenField = document.createElement("input");
      tokenField.type = "hidden";
      tokenField.name = transport.tokenField;
      form.appendChild(tokenField);
    }
    tokenField.value = transport.token || "";
  }

  function decorateForms() {
    document.querySelectorAll("form").forEach(ensureFormTransportFields);
  }

  function handleBlockedAction(event, message) {
    event.preventDefault();
    event.stopPropagation();
    if (typeof event.stopImmediatePropagation === "function") {
      event.stopImmediatePropagation();
    }

    showToast(message || i18n("actionBlocked", "This admin screen is running in demo mode. Changes are disabled for your account."), true);
  }

  function handleClick(event) {
    if (!getData().disableControls) {
      return;
    }

    var target = event.target && event.target.closest ? event.target.closest(INTERACTIVE_SELECTOR) : null;
    if (!target) {
      return;
    }

    if (!isLikelyMutatingControl(target)) {
      return;
    }

    handleBlockedAction(event, i18n("actionBlocked", "This admin screen is running in demo mode. Changes are disabled for your account."));
  }

  function handleSubmit(event) {
    var form = event.target;
    ensureFormTransportFields(form);

    if (!getData().blockFormSubmissions) {
      return;
    }

    if (!isMutatingForm(form)) {
      return;
    }

    handleBlockedAction(event, i18n("formBlocked", "Form submission blocked by Demo Lock."));
  }

  function getRequestAction(body) {
    if (!body) {
      return "";
    }

    if (typeof URLSearchParams !== "undefined" && body instanceof URLSearchParams) {
      return normalizeText(body.get("action"));
    }

    if (typeof FormData !== "undefined" && body instanceof FormData) {
      return normalizeText(body.get("action"));
    }

    if (typeof body === "string") {
      try {
        return normalizeText(new URLSearchParams(body).get("action"));
      } catch (error) {
        return "";
      }
    }

    if (typeof body === "object" && body.action) {
      return normalizeText(body.action);
    }

    return "";
  }

  function getRequestMeta(url, method, body) {
    var normalizedMethod = normalizeText(method || "get").toUpperCase();
    var meta = {
      method: normalizedMethod,
      isWrite: normalizedMethod !== "GET" && normalizedMethod !== "HEAD" && normalizedMethod !== "OPTIONS",
      isSameOrigin: false,
      isAjax: false,
      isRest: false,
      isAdmin: false,
      action: "",
      hasMutationHint: false,
      url: ""
    };

    if (!url) {
      return meta;
    }

    try {
      var resolvedUrl = new URL(url, window.location.href);
      meta.url = resolvedUrl.href;
      meta.isSameOrigin = resolvedUrl.origin === window.location.origin;
      meta.isAjax = resolvedUrl.pathname.indexOf("/wp-admin/admin-ajax.php") !== -1;
      meta.isRest = resolvedUrl.pathname.indexOf("/wp-json/") !== -1;
      meta.isAdmin = resolvedUrl.pathname.indexOf("/wp-admin/") !== -1;
      meta.action = getRequestAction(body) || getUrlAction(resolvedUrl.href);
      meta.hasMutationHint = looksMutatingAction(meta.action, false);
    } catch (error) {
      return meta;
    }

    return meta;
  }

  function shouldAttachTransport(meta) {
    return !!(meta && meta.isSameOrigin && (meta.isAjax || meta.isRest || meta.isAdmin) && (meta.isWrite || meta.hasMutationHint));
  }

  function shouldPreBlockRequest(meta) {
    if (!meta || (!meta.isWrite && !meta.hasMutationHint)) {
      return false;
    }

    if (meta.isRest) {
      return !!getData().blockRestRequests;
    }

    if (meta.isAjax) {
      if (!getData().blockAjaxRequests) {
        return false;
      }

      if (meta.method === "GET") {
        return !!meta.hasMutationHint;
      }

      return !!meta.isWrite;
    }

    if (meta.isAdmin) {
      return !!getData().blockFormSubmissions;
    }

    return false;
  }

  function applyTransportHeaders(headers) {
    var transport = getTransport();
    if (!transport.ruleHeader || !transport.tokenHeader || !transport.ruleId || !transport.token) {
      return headers;
    }

    headers.set(transport.ruleHeader, String(transport.ruleId));
    headers.set(transport.tokenHeader, String(transport.token));
    return headers;
  }

  function createSyntheticEvent(type) {
    if (typeof Event === "function") {
      return new Event(type);
    }

    if (document.createEvent) {
      var event = document.createEvent("Event");
      event.initEvent(type, false, false);
      return event;
    }

    return null;
  }

  function dispatchSyntheticXhrEvent(xhr, type) {
    var event = createSyntheticEvent(type);

    if (typeof xhr.dispatchEvent === "function" && event) {
      try {
        xhr.dispatchEvent(event);
      } catch (error) {
        // Ignore synthetic event dispatch errors on unusual XHR implementations.
      }
    }

    var handler = xhr["on" + type];
    if (typeof handler === "function") {
      try {
        handler.call(xhr, event || { type: type, target: xhr });
      } catch (error) {
        // Ignore callback errors so the blocking shim never breaks the page.
      }
    }
  }

  function tryDefineXhrProperty(xhr, propertyName, value) {
    try {
      Object.defineProperty(xhr, propertyName, {
        configurable: true,
        get: function () {
          return value;
        }
      });
    } catch (error) {
      // Native XHR properties are not always configurable.
    }
  }

  function emitBlockedXmlHttpRequest(xhr, message) {
    var payload = JSON.stringify({
      success: false,
      message: message
    });

    window.setTimeout(function () {
      xhr.__polangerShieldDemoLockBlocked = true;
      xhr.__polangerShieldDemoLockBlockedMessage = message;

      tryDefineXhrProperty(xhr, "readyState", 4);
      tryDefineXhrProperty(xhr, "status", 403);
      tryDefineXhrProperty(xhr, "statusText", "Forbidden");
      tryDefineXhrProperty(xhr, "responseText", payload);
      tryDefineXhrProperty(xhr, "response", payload);

      dispatchSyntheticXhrEvent(xhr, "readystatechange");
      dispatchSyntheticXhrEvent(xhr, "error");
      dispatchSyntheticXhrEvent(xhr, "loadend");
    }, 0);
  }

  function wrapFetch() {
    if (typeof window.fetch !== "function") {
      return;
    }

    var originalFetch = window.fetch;
    window.fetch = function (input, init) {
      var requestUrl = typeof input === "string" ? input : (input && input.url ? input.url : "");
      var requestMethod = (init && init.method) || (input && input.method) || "GET";
      var requestBody = init && typeof init.body !== "undefined" ? init.body : (input && input.body ? input.body : null);
      var meta = getRequestMeta(requestUrl, requestMethod, requestBody);
      var options = init ? $.extend({}, init) : {};

      if (shouldAttachTransport(meta)) {
        var headers = new Headers(options.headers || (input && input.headers ? input.headers : undefined));
        options.headers = applyTransportHeaders(headers);
      }

      if (shouldPreBlockRequest(meta)) {
        showToast(i18n("actionBlocked", "This admin screen is running in demo mode. Changes are disabled for your account."), true);

        if (typeof Response === "function") {
          return Promise.resolve(new Response(JSON.stringify({
            success: false,
            message: i18n("actionBlocked", "This admin screen is running in demo mode. Changes are disabled for your account.")
          }), {
            status: 403,
            headers: {
              "Content-Type": "application/json"
            }
          }));
        }

        return Promise.reject(new Error(i18n("actionBlocked", "This admin screen is running in demo mode. Changes are disabled for your account.")));
      }

      return originalFetch.call(window, input, options);
    };
  }

  function wrapXmlHttpRequest() {
    if (typeof window.XMLHttpRequest === "undefined") {
      return;
    }

    var originalOpen = window.XMLHttpRequest.prototype.open;
    var originalSend = window.XMLHttpRequest.prototype.send;

    window.XMLHttpRequest.prototype.open = function (method, url) {
      this.__polangerShieldDemoLockMethod = method;
      this.__polangerShieldDemoLockUrl = url;
      return originalOpen.apply(this, arguments);
    };

    window.XMLHttpRequest.prototype.send = function (body) {
      var meta = getRequestMeta(this.__polangerShieldDemoLockUrl, this.__polangerShieldDemoLockMethod, body);

      if (shouldPreBlockRequest(meta)) {
        var message = i18n("actionBlocked", "This admin screen is running in demo mode. Changes are disabled for your account.");
        showToast(message, true);
        emitBlockedXmlHttpRequest(this, message);
        return;
      }

      if (shouldAttachTransport(meta)) {
        try {
          this.setRequestHeader(getTransport().ruleHeader, String(getTransport().ruleId || ""));
          this.setRequestHeader(getTransport().tokenHeader, String(getTransport().token || ""));
        } catch (error) {
          // Ignore header assignment errors for non-standard transports.
        }
      }

      return originalSend.apply(this, arguments);
    };
  }

  function setupObserver() {
    if (typeof MutationObserver === "undefined" || !document.body) {
      return;
    }

    observer = new MutationObserver(function () {
      decorateControls();
      decorateForms();
    });

    observer.observe(document.body, {
      childList: true,
      subtree: true
    });
  }

  $(document).on("click", ".polanger-shield-demo-lock-banner__dismiss", function (event) {
    event.preventDefault();
    persistBannerCollapsed(true);
    setBannerCollapsed(true);
  });

  $(document).on("click", ".polanger-shield-demo-lock-banner-toggle", function (event) {
    event.preventDefault();
    persistBannerCollapsed(false);
    setBannerCollapsed(false);
  });

  $(function () {
    document.body.classList.add("polanger-shield-demo-lock-active");
    createBanner();
    decorateControls();
    decorateForms();
    setupObserver();
    wrapFetch();
    wrapXmlHttpRequest();
    document.addEventListener("click", handleClick, true);
    document.addEventListener("submit", handleSubmit, true);
  });
})(jQuery);
