/**
 * Customer Portal UI Helpers
 *
 * Vanilla-JS helpers shared by `customerPortal.js` (free) and
 * `customerPortalPro.js` (pro):
 *
 *  - confirmDialog(opts)        Promise-based confirm modal that replaces
 *                               native window.confirm/alert prompts.
 *  - portalToast(msg, type)     Lightweight floating notification that
 *                               replaces the alert() success/error popups.
 *  - setButtonLoading($btn, on) Toggle a "Wait…" spinner state on a jQuery
 *                               button. Reversible.
 *
 * The modal markup uses the existing `arraysubs-modal*` BEM classes so it
 * inherits portal modal styling, with a small extra layer of styles for
 * the danger variant in `_confirm-dialog.scss`.
 *
 * @package ArraySubs
 */

const $ = window.jQuery;

const SPINNER_SVG = `
  <svg class="arraysubs-portal-spinner" width="14" height="14" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
    <circle cx="12" cy="12" r="10" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-dasharray="32" stroke-dashoffset="8" />
  </svg>
`;

const escapeHtml = (text) => {
  const div = document.createElement("div");
  div.textContent = String(text == null ? "" : text);
  return div.innerHTML;
};

const ensureContainer = () => {
  let container = document.getElementById("arraysubs-portal-confirm-root");
  if (!container) {
    container = document.createElement("div");
    container.id = "arraysubs-portal-confirm-root";
    document.body.appendChild(container);
  }
  return container;
};

const hideBackgroundForDialog = (activeRoot) => {
  const hidden = [];

  Array.from(document.body.children).forEach((element) => {
    if (element === activeRoot || element.contains(activeRoot)) {
      return;
    }

    hidden.push({
      element,
      ariaHidden: element.getAttribute("aria-hidden"),
      inert: "inert" in element ? element.inert : null,
    });

    element.setAttribute("aria-hidden", "true");
    if ("inert" in element) {
      element.inert = true;
    }
  });

  return () => {
    hidden.forEach(({ element, ariaHidden, inert }) => {
      if (ariaHidden === null) {
        element.removeAttribute("aria-hidden");
      } else {
        element.setAttribute("aria-hidden", ariaHidden);
      }

      if (inert !== null && "inert" in element) {
        element.inert = inert;
      }
    });
  };
};

const ensureToastContainer = () => {
  let container = document.getElementById("arraysubs-portal-toast-root");
  if (!container) {
    container = document.createElement("div");
    container.id = "arraysubs-portal-toast-root";
    container.className = "arraysubs-portal-toast-container";
    document.body.appendChild(container);
  }
  return container;
};

/**
 * Open a Promise-based confirmation dialog.
 *
 * Resolves true if the user confirmed, false otherwise.
 *
 * @param {Object} opts
 * @param {string} opts.title          Dialog title
 * @param {string} opts.message        Dialog message (plain text)
 * @param {string} [opts.confirmText]  Confirm button text
 * @param {string} [opts.cancelText]   Cancel button text
 * @param {string} [opts.variant]      "info" | "warning" | "danger"
 * @param {string} [opts.loadingText]  Confirm button text while running
 * @param {Function} [opts.onConfirm]  Async callback. While it runs the
 *                                     confirm button shows a spinner.
 *                                     Resolve = close + resolve(true).
 *                                     Throw = stay open + show error.
 */
export function confirmDialog(opts = {}) {
  const {
    title = "",
    message = "",
    confirmText = "Confirm",
    cancelText = "Cancel",
    variant = "info",
    loadingText = "Wait…",
    onConfirm,
  } = opts;

  return new Promise((resolve) => {
    const root = ensureContainer();

    const wrapper = document.createElement("div");
    wrapper.className = `arraysubs-modal arraysubs-portal-confirm arraysubs-portal-confirm--${variant}`;
    wrapper.setAttribute("role", "dialog");
    wrapper.setAttribute("aria-modal", "true");
    wrapper.style.display = "none";

    wrapper.innerHTML = `
      <div class="arraysubs-modal__backdrop"></div>
      <div class="arraysubs-modal__container arraysubs-modal__container--confirm">
        <div class="arraysubs-modal__content">
          ${
            title
              ? `<div class="arraysubs-modal__header"><h3 class="arraysubs-modal__title">${escapeHtml(
                  title,
                )}</h3></div>`
              : ""
          }
          <div class="arraysubs-modal__body arraysubs-portal-confirm__body">
            <p class="arraysubs-modal__message">${escapeHtml(message)}</p>
            <p class="arraysubs-portal-confirm__error" hidden></p>
          </div>
          <div class="arraysubs-modal__footer">
            <button type="button" class="button arraysubs-modal__cancel-btn arraysubs-portal-confirm__cancel">${escapeHtml(
              cancelText,
            )}</button>
            <button type="button" class="button button-primary arraysubs-modal__confirm-btn arraysubs-portal-confirm__confirm${
              variant === "danger" ? " arraysubs-btn--danger" : ""
            }">${escapeHtml(confirmText)}</button>
          </div>
        </div>
      </div>
    `;

    root.appendChild(wrapper);
    const restoreBackground = hideBackgroundForDialog(root);
    const $modal = $(wrapper);
    const $confirmBtn = $modal.find(".arraysubs-portal-confirm__confirm");
    const $cancelBtn = $modal.find(".arraysubs-portal-confirm__cancel");
    const $error = $modal.find(".arraysubs-portal-confirm__error");
    const $backdrop = $modal.find(".arraysubs-modal__backdrop");
    const previouslyFocused = document.activeElement;

    let settled = false;
    let isLoading = false;

    const close = (result) => {
      if (settled) return;
      settled = true;
      $modal.fadeOut(150, () => {
        $modal.remove();
        restoreBackground();
        $("body").removeClass("arraysubs-modal-open");
        if (previouslyFocused && previouslyFocused.focus) {
          previouslyFocused.focus();
        }
        resolve(result);
      });
      $(document).off("keydown.arraysubsPortalConfirm");
    };

    const onCancel = () => {
      if (isLoading) return;
      close(false);
    };

    const onConfirmClick = async () => {
      if (typeof onConfirm !== "function") {
        close(true);
        return;
      }

      isLoading = true;
      $error.attr("hidden", true).text("");
      setButtonLoading($confirmBtn, true, loadingText);
      $cancelBtn.prop("disabled", true);

      try {
        await onConfirm();
        close(true);
      } catch (error) {
        const msg =
          error && error.message
            ? error.message
            : "Something went wrong. Please try again.";
        $error.text(msg).removeAttr("hidden");
        setButtonLoading($confirmBtn, false);
        $cancelBtn.prop("disabled", false);
        isLoading = false;
      }
    };

    $confirmBtn.on("click", onConfirmClick);
    $cancelBtn.on("click", onCancel);
    $backdrop.on("click", onCancel);

    $(document).on("keydown.arraysubsPortalConfirm", (e) => {
      if (e.key === "Escape") onCancel();
      if (e.key === "Enter" && !isLoading) onConfirmClick();
    });

    $("body").addClass("arraysubs-modal-open");
    $modal.fadeIn(150, () => {
      $confirmBtn.trigger("focus");
    });
  });
}

/**
 * Prompt the user for a single value via a UI modal (replaces native
 * window.prompt). Resolves to the entered string, or `null` if cancelled.
 *
 * @param {Object} opts
 * @param {string} opts.title
 * @param {string} [opts.label]        Label rendered above the input
 * @param {string} [opts.message]      Help message below the input
 * @param {string} [opts.defaultValue] Pre-filled value
 * @param {string} [opts.placeholder]
 * @param {"text"|"number"} [opts.inputType] Default "text"
 * @param {number} [opts.min]          For number inputs
 * @param {number} [opts.max]          For number inputs
 * @param {string} [opts.confirmText]
 * @param {string} [opts.cancelText]
 * @param {function} [opts.validate]   Called with the value; return string
 *                                     to display as inline error, or null
 *                                     to allow submission.
 */
export function promptDialog(opts = {}) {
  const {
    title = "",
    label = "",
    message = "",
    defaultValue = "",
    placeholder = "",
    inputType = "text",
    min,
    max,
    confirmText = "OK",
    cancelText = "Cancel",
    validate,
  } = opts;

  return new Promise((resolve) => {
    const root = ensureContainer();
    const wrapper = document.createElement("div");
    wrapper.className = "arraysubs-modal arraysubs-portal-confirm";
    wrapper.setAttribute("role", "dialog");
    wrapper.setAttribute("aria-modal", "true");
    wrapper.style.display = "none";

    const minAttr = min != null ? ` min="${escapeHtml(min)}"` : "";
    const maxAttr = max != null ? ` max="${escapeHtml(max)}"` : "";

    wrapper.innerHTML = `
      <div class="arraysubs-modal__backdrop"></div>
      <div class="arraysubs-modal__container arraysubs-modal__container--confirm">
        <div class="arraysubs-modal__content">
          ${
            title
              ? `<div class="arraysubs-modal__header"><h3 class="arraysubs-modal__title">${escapeHtml(
                  title,
                )}</h3></div>`
              : ""
          }
          <div class="arraysubs-modal__body arraysubs-portal-confirm__body">
            ${
              label
                ? `<label class="arraysubs-modal__label arraysubs-portal-prompt__label">${escapeHtml(
                    label,
                  )}</label>`
                : ""
            }
            <input type="${escapeHtml(
              inputType,
            )}" class="arraysubs-modal__input arraysubs-portal-prompt__input"${minAttr}${maxAttr} placeholder="${escapeHtml(
              placeholder,
            )}" />
            ${
              message
                ? `<p class="arraysubs-modal__message" style="margin-top:8px;">${escapeHtml(
                    message,
                  )}</p>`
                : ""
            }
            <p class="arraysubs-portal-confirm__error" hidden></p>
          </div>
          <div class="arraysubs-modal__footer">
            <button type="button" class="button arraysubs-modal__cancel-btn arraysubs-portal-confirm__cancel">${escapeHtml(
              cancelText,
            )}</button>
            <button type="button" class="button button-primary arraysubs-modal__confirm-btn arraysubs-portal-confirm__confirm">${escapeHtml(
              confirmText,
            )}</button>
          </div>
        </div>
      </div>
    `;

    root.appendChild(wrapper);
    const restoreBackground = hideBackgroundForDialog(root);
    const $modal = $(wrapper);
    const $input = $modal.find(".arraysubs-portal-prompt__input");
    const $confirmBtn = $modal.find(".arraysubs-portal-confirm__confirm");
    const $cancelBtn = $modal.find(".arraysubs-portal-confirm__cancel");
    const $error = $modal.find(".arraysubs-portal-confirm__error");
    const $backdrop = $modal.find(".arraysubs-modal__backdrop");
    const previouslyFocused = document.activeElement;

    $input.val(defaultValue);

    let settled = false;
    const close = (result) => {
      if (settled) return;
      settled = true;
      $modal.fadeOut(150, () => {
        $modal.remove();
        restoreBackground();
        $("body").removeClass("arraysubs-modal-open");
        if (previouslyFocused && previouslyFocused.focus) {
          previouslyFocused.focus();
        }
        resolve(result);
      });
      $(document).off("keydown.arraysubsPortalPrompt");
    };

    const onCancel = () => close(null);

    const onConfirmClick = () => {
      const value = String($input.val() || "").trim();
      if (typeof validate === "function") {
        const err = validate(value);
        if (err) {
          $error.text(err).removeAttr("hidden");
          $input.trigger("focus");
          return;
        }
      }
      close(value);
    };

    $confirmBtn.on("click", onConfirmClick);
    $cancelBtn.on("click", onCancel);
    $backdrop.on("click", onCancel);

    $(document).on("keydown.arraysubsPortalPrompt", (e) => {
      if (e.key === "Escape") onCancel();
      if (e.key === "Enter") onConfirmClick();
    });

    $("body").addClass("arraysubs-modal-open");
    $modal.fadeIn(150, () => {
      $input.trigger("focus").trigger("select");
    });
  });
}

/**
 * Show a brief floating toast (replaces alert() for success/error).
 *
 * @param {string} message
 * @param {"success"|"error"|"info"} [type]
 * @param {number} [duration]  ms; default 3500
 */
export function portalToast(message, type = "success", duration = 3500) {
  if (!message) return;
  const container = ensureToastContainer();
  const toast = document.createElement("div");
  toast.className = `arraysubs-portal-toast arraysubs-portal-toast--${type}`;
  toast.setAttribute("role", type === "error" ? "alert" : "status");
  toast.innerHTML = `
    <span class="arraysubs-portal-toast__icon" aria-hidden="true">${
      type === "error" ? "✕" : type === "info" ? "ℹ" : "✓"
    }</span>
    <span class="arraysubs-portal-toast__message"></span>
  `;
  toast.querySelector(".arraysubs-portal-toast__message").textContent = message;
  container.appendChild(toast);

  // animate in
  requestAnimationFrame(() => {
    toast.classList.add("arraysubs-portal-toast--visible");
  });

  const removeToast = () => {
    toast.classList.remove("arraysubs-portal-toast--visible");
    setTimeout(() => {
      if (toast.parentNode) toast.parentNode.removeChild(toast);
    }, 250);
  };

  toast.addEventListener("click", removeToast);
  setTimeout(removeToast, Math.max(1500, duration));
}

/**
 * Toggle a "Wait…" spinner state on a jQuery button.
 *
 * Pass `loading=true` to replace the button label with a spinner + the
 * provided text. Pass `loading=false` to restore the original markup.
 *
 * Stores the original innerHTML on `data-arraysubs-original-html` so calls
 * are idempotent / nestable safely.
 *
 * @param {jQuery|HTMLElement} btn
 * @param {boolean} loading
 * @param {string} [loadingText]
 */
export function setButtonLoading(btn, loading, loadingText = "Wait…") {
  const $btn = btn instanceof $ ? btn : $(btn);
  if (!$btn.length) return;

  if (loading) {
    if (!$btn.data("arraysubs-original-html")) {
      $btn.data("arraysubs-original-html", $btn.html());
    }
    $btn.prop("disabled", true).addClass("arraysubs-btn--loading");
    $btn.html(`${SPINNER_SVG}<span>${escapeHtml(loadingText)}</span>`);
  } else {
    const original = $btn.data("arraysubs-original-html");
    if (original != null) {
      $btn.html(original);
      $btn.removeData("arraysubs-original-html");
    }
    $btn.prop("disabled", false).removeClass("arraysubs-btn--loading");
  }
}
