(function initFivaHardPaste(global) {
  "use strict";

  if (global.FIVA?.HardPaste) {
    if (typeof module !== "undefined" && module.exports) module.exports = global.FIVA.HardPaste;
    return;
  }

  function config() {
    return global.FIVA?.RuntimeConfig || {
      retryCount: 2,
      typingDelayMinMs: 30,
      typingDelayMaxMs: 80,
      fastActionDelayMs: 120
    };
  }

  function fieldDetector() {
    return global.FIVA?.DetectFieldType || null;
  }

  function wait(ms = 50, signal) {
    if (signal?.aborted) return Promise.resolve(false);
    return new Promise((resolve) => {
      const timer = setTimeout(() => {
        signal?.removeEventListener?.("abort", onAbort);
        resolve(true);
      }, ms);
      function onAbort() {
        clearTimeout(timer);
        resolve(false);
      }
      signal?.addEventListener?.("abort", onAbort, { once: true });
    });
  }

  function delay(signal) {
    const runtime = config();
    const min = Number(runtime.typingDelayMinMs || 30);
    const max = Number(runtime.typingDelayMaxMs || 80);
    return wait(Math.floor(min + (Math.random() * Math.max(1, max - min))), signal);
  }

  function fire(control, type, init = {}) {
    control.dispatchEvent(new Event(type, { bubbles: true, cancelable: true, ...init }));
  }

  function key(control, type, keyName, options = {}) {
    control.dispatchEvent(new KeyboardEvent(type, {
      bubbles: true,
      cancelable: true,
      key: keyName,
      code: options.code || keyName,
      ctrlKey: options.ctrlKey === true,
      metaKey: options.metaKey === true
    }));
  }

  function inputEvent(control, value, inputType = "insertFromPaste") {
    try {
      control.dispatchEvent(new InputEvent("beforeinput", {
        bubbles: true,
        cancelable: true,
        data: value,
        inputType
      }));
    } catch (_) {
      fire(control, "beforeinput");
    }
    fire(control, "input");
  }

  function nativeSetter(control, value) {
    const proto = control.tagName === "TEXTAREA" ? HTMLTextAreaElement.prototype : HTMLInputElement.prototype;
    const descriptor = Object.getOwnPropertyDescriptor(proto, "value");
    if (descriptor?.set) descriptor.set.call(control, value);
    else control.value = value;
  }

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

  function defaultVerify(control, expected) {
    return normalize(control.value) === normalize(expected);
  }

  function trace(options = {}, message, level = "info") {
    if (typeof options.onTrace === "function") options.onTrace(message, level);
  }

  function activate(control) {
    control.scrollIntoView({ block: "center", inline: "nearest" });
    control.dispatchEvent(new MouseEvent("mouseover", { bubbles: true, cancelable: true, view: global }));
    control.dispatchEvent(new MouseEvent("mousedown", { bubbles: true, cancelable: true, view: global }));
    control.focus({ preventScroll: true });
    control.click();
    control.dispatchEvent(new MouseEvent("mouseup", { bubbles: true, cancelable: true, view: global }));
  }

  async function clear(control, signal) {
    key(control, "keydown", "a", { ctrlKey: true, code: "KeyA" });
    key(control, "keypress", "a", { ctrlKey: true, code: "KeyA" });
    key(control, "keyup", "a", { ctrlKey: true, code: "KeyA" });
    await delay(signal);
    key(control, "keydown", "Backspace", { code: "Backspace" });
    nativeSetter(control, "");
    inputEvent(control, "", "deleteContentBackward");
    key(control, "keyup", "Backspace", { code: "Backspace" });
    key(control, "keydown", "Delete", { code: "Delete" });
    key(control, "keyup", "Delete", { code: "Delete" });
    fire(control, "change");
    try {
      if (typeof control.setSelectionRange === "function") control.setSelectionRange(0, 0);
    } catch (_) {
      // Specialized inputs may reject text selection.
    }
    await delay(signal);
  }

  async function hardPaste(control, value, options = {}) {
    const expected = String(value || "");
    const signal = options.signal;
    const verify = typeof options.verify === "function" ? options.verify : defaultVerify;
    if (!control || !expected || signal?.aborted) return false;

    activate(control);
    await delay(signal);
    await clear(control, signal);

    key(control, "keydown", "v", { ctrlKey: true, code: "KeyV" });
    key(control, "keypress", "v", { ctrlKey: true, code: "KeyV" });
    nativeSetter(control, expected);
    inputEvent(control, expected, "insertFromPaste");
    key(control, "keyup", "v", { ctrlKey: true, code: "KeyV" });
    fire(control, "change");
    await delay(signal);
    control.blur();
    fire(control, "blur");
    return verify(control, expected);
  }

  async function simulateTyping(control, value, options = {}) {
    const expected = String(value || "");
    const signal = options.signal;
    const verify = typeof options.verify === "function" ? options.verify : defaultVerify;
    if (!control || !expected || signal?.aborted) return false;

    activate(control);
    await delay(signal);
    await clear(control, signal);

    for (const char of expected) {
      if (signal?.aborted) return false;
      const currentValue = String(control.value || "");
      const start = Number.isFinite(control.selectionStart) ? control.selectionStart : currentValue.length;
      const end = Number.isFinite(control.selectionEnd) ? control.selectionEnd : start;
      const code = char.length === 1 && /[a-z]/i.test(char) ? `Key${char.toUpperCase()}` : char;
      key(control, "keydown", char, { code });
      key(control, "keypress", char, { code });
      const nextValue = `${currentValue.slice(0, start)}${char}${currentValue.slice(end)}`;
      nativeSetter(control, nextValue);
      try {
        if (typeof control.setSelectionRange === "function") {
          const caret = start + char.length;
          control.setSelectionRange(caret, caret);
        }
      } catch (_) {
        // Specialized inputs may reject text selection.
      }
      inputEvent(control, char, "insertText");
      key(control, "keyup", char, { code });
      await delay(signal);
    }

    fire(control, "change");
    await delay(signal);
    control.blur();
    fire(control, "blur");
    return verify(control, expected);
  }

  async function fill(control, value, options = {}) {
    const type = fieldDetector()?.detectFieldType?.(control) || "";
    if (type !== "textbox" && type !== "textarea") return false;
    const expected = String(value || "");
    const signal = options.signal;
    const verify = typeof options.verify === "function" ? options.verify : defaultVerify;
    const attempts = Math.max(1, Math.min(Number(options.attempts || config().retryCount || 2), 2));
    if (!expected || signal?.aborted) return false;

    const wasReadOnly = control.readOnly === true;
    if (wasReadOnly) {
      control.readOnly = false;
      control.removeAttribute("readonly");
    }

    try {
      trace(options, "hard paste executing");
      for (let attempt = 0; attempt < attempts; attempt += 1) {
        if (signal?.aborted) return false;
        const pasted = await hardPaste(control, expected, { ...options, verify });
        if (pasted) {
          trace(options, "hard-paste success");
          return true;
        }
        trace(options, `hard-paste retry ${attempt + 1}`, "warn");
      }
      trace(options, "typing fallback executing", "warn");
      const typed = await simulateTyping(control, expected, { ...options, verify });
      trace(options, typed ? "typing fallback success" : "hard-paste failed", typed ? "info" : "error");
      return typed;
    } finally {
      if (wasReadOnly) {
        control.readOnly = true;
        control.setAttribute("readonly", "readonly");
      }
    }
  }

  const HardPaste = Object.freeze({
    clear,
    fill,
    hardPaste,
    nativeSetter,
    smartInputInsert: fill,
    simulateTyping,
    wait
  });

  global.FIVA = Object.assign(global.FIVA || {}, { HardPaste });

  if (typeof module !== "undefined" && module.exports) {
    module.exports = HardPaste;
  }
})(typeof globalThis !== "undefined" ? globalThis : this);
