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

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

  const MAX_RETRIES = 3;
  const MAX_WAIT_PER_FIELD = 8000;
  const STABILIZE_MIN_MS = 800;
  const STABILIZE_MAX_MS = 1500;
  const PLACEHOLDER_PATTERN = /\b(select|choose|loading|wait|please wait|please select|none)\b/i;
  const LOADING_PATTERN = /\b(loading|wait|please wait)\b/i;

  function smart() {
    return global.FIVA?.SelectSmartOption || null;
  }

  function wait(ms = 80, 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 humanDelay(min = STABILIZE_MIN_MS, max = STABILIZE_MAX_MS, signal) {
    const duration = Math.floor(min + (Math.random() * Math.max(1, max - min)));
    return wait(duration, signal);
  }

  function labelFor(fieldKey = "") {
    return String(fieldKey || "dropdown")
      .replace(/([A-Z])/g, " $1")
      .replace(/^./, (letter) => letter.toUpperCase())
      .trim();
  }

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

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

  function optionText(option) {
    return String(option?.text || option?.textContent || option?.label || option?.innerText || "").replace(/\s+/g, " ").trim();
  }

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

  function isPlaceholderOption(option) {
    const text = optionText(option);
    const value = optionValue(option);
    if (!value && !text) return true;
    return PLACEHOLDER_PATTERN.test(text) || PLACEHOLDER_PATTERN.test(value) || /^(0|-1)$/i.test(value);
  }

  function validOptions(select) {
    const smartOptions = smart()?.validOptions?.(select);
    if (Array.isArray(smartOptions)) return smartOptions.filter((option) => !isPlaceholderOption(option));
    return Array.from(select?.options || []).filter((option) => !isPlaceholderOption(option));
  }

  function availableOptionLabels(select) {
    return Array.from(select?.options || [])
      .map((option) => optionText(option) || optionValue(option))
      .filter(Boolean);
  }

  function snapshot(select) {
    if (!select) return {
      html: "",
      count: 0,
      disabled: true,
      signature: ""
    };
    return {
      html: select.innerHTML || "",
      count: select.options?.length || 0,
      disabled: select.disabled === true,
      signature: availableOptionLabels(select).join("|")
    };
  }

  function hasSnapshotChanged(select, before) {
    if (!select) return false;
    const current = snapshot(select);
    return current.html !== before.html
      || current.count !== before.count
      || current.disabled !== before.disabled
      || current.signature !== before.signature;
  }

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

  async function waitForDropdownReady(select, options = {}) {
    const timeout = Math.max(400, Math.min(Number(options.timeout || MAX_WAIT_PER_FIELD), MAX_WAIT_PER_FIELD));
    const quiet = Math.max(160, Number(options.quiet || 500));
    const startedAt = Date.now();
    const signal = options.signal;
    let stableSince = Date.now();
    let lastSignature = snapshot(select).signature;
    let lastDisabled = select?.disabled === true;

    const observer = typeof MutationObserver !== "undefined" && select
      ? new MutationObserver(() => {
        stableSince = Date.now();
        lastSignature = snapshot(select).signature;
        lastDisabled = select.disabled === true;
      })
      : null;
    observer?.observe(select, { childList: true, subtree: true, attributes: true });

    try {
      while (Date.now() - startedAt < timeout) {
        if (signal?.aborted) return { ready: false, reason: "stopped_manually", count: 0 };
        if (!select) return { ready: false, reason: "selector_missing", count: 0 };

        const current = snapshot(select);
        if (current.signature !== lastSignature || current.disabled !== lastDisabled) {
          lastSignature = current.signature;
          lastDisabled = current.disabled;
          stableSince = Date.now();
        }

        const optionsList = validOptions(select);
        const loading = Array.from(select.options || []).some((option) => LOADING_PATTERN.test(optionText(option)) || LOADING_PATTERN.test(optionValue(option)));
        if (!select.disabled && !loading && optionsList.length > 0 && Date.now() - stableSince >= quiet) {
          return { ready: true, reason: "", count: optionsList.length };
        }

        await wait(120, signal);
      }
      return { ready: false, reason: validOptions(select).length ? "verification_failed" : "dropdown_not_loaded", count: validOptions(select).length };
    } finally {
      observer?.disconnect();
    }
  }

  function candidatesFor(fieldKey = "", value = "") {
    const base = smart()?.candidatesFor?.(fieldKey, value) || [normalize(value)];
    const candidates = new Set(base.map(normalize));
    const source = normalize(value);
    if (fieldKey === "country" && /BANGLADESH|BD|BANGLADESHI/.test(source)) {
      candidates.add("BANGLADESH");
      candidates.add("BD");
    }
    if (fieldKey === "mission" && /DHAKA|BANGLADESH/.test(source)) {
      candidates.add("DHAKA");
      candidates.add("BANGLADESH DHAKA");
      candidates.add("BANGLADESHDHAKA");
    }
    if (fieldKey === "nationality" && /BANGLADESH|BANGLADESHI/.test(source)) {
      candidates.add("BANGLADESH");
      candidates.add("BANGLADESHI");
    }
    return Array.from(candidates).filter(Boolean);
  }

  function matchOption(select, options = {}) {
    const smartMatch = smart()?.matchOption?.(select, options);
    if (smartMatch?.option) return smartMatch;

    const fieldKey = options.fieldKey || "";
    const candidates = candidatesFor(fieldKey, options.value || "");
    const list = validOptions(select);
    const strategies = [
      ["exact_value", (option, candidate) => normalize(optionValue(option)) === normalize(candidate)],
      ["exact_text", (option, candidate) => normalize(optionText(option)) === normalize(candidate)],
      ["normalized_exact", (option, candidate) => compactMatch(optionValue(option)) === compactMatch(candidate) || compactMatch(optionText(option)) === compactMatch(candidate)],
      ["contains", (option, candidate) => {
        const needle = compactMatch(candidate);
        const parts = [compactMatch(optionValue(option)), compactMatch(optionText(option))]
          .filter((part) => part.length >= 2);
        return needle.length >= 2 && (
          parts.some((part) => part.includes(needle))
          || parts.some((part) => needle.includes(part))
        );
      }]
    ];

    for (const [strategy, matcher] of strategies) {
      for (const candidate of candidates) {
        const option = list.find((item) => matcher(item, candidate));
        if (option) return { option, strategy, candidates, optionCount: list.length };
      }
    }

    return { option: null, strategy: "", candidates, optionCount: list.length };
  }

  function fireMouse(select, type) {
    select.dispatchEvent(new MouseEvent(type, {
      bubbles: true,
      cancelable: true,
      view: global
    }));
  }

  function fireEvent(select, type) {
    select.dispatchEvent(new Event(type, { bubbles: true, cancelable: true }));
  }

  async function humanOpen(select, options = {}) {
    select.scrollIntoView({ block: "center", inline: "nearest" });
    fireMouse(select, "mouseenter");
    fireMouse(select, "mousemove");
    fireMouse(select, "mousedown");
    select.focus({ preventScroll: true });
    select.click();
    await wait(120, options.signal);
  }

  async function applyHumanSelection(select, option, options = {}) {
    if (!select || !option || options.signal?.aborted) return false;
    await humanOpen(select, options);
    if (options.signal?.aborted) return false;
    select.selectedIndex = option.index;
    option.selected = true;
    if (optionValue(option)) select.value = option.value;
    fireEvent(select, "input");
    fireEvent(select, "change");
    select.blur();
    fireEvent(select, "blur");
    return true;
  }

  function verifySelection(select, option, options = {}) {
    if (!select || !option) return false;
    const selected = select.options[select.selectedIndex];
    if (!selected || select.selectedIndex <= 0) return false;
    if (selected === option || optionValue(selected) === optionValue(option)) return true;
    const matched = matchOption(select, options);
    return Boolean(matched.option && (selected === matched.option || optionValue(selected) === optionValue(matched.option)));
  }

  async function waitForAspNetUpdate(dependentSelect, before, options = {}) {
    if (!dependentSelect) return { refreshed: true, reason: "no_dependent_select" };
    const timeout = Math.max(800, Math.min(Number(options.timeout || 8000), 8000));
    const startedAt = Date.now();
    const signal = options.signal;
    trace(options, "Waiting ASP.NET update");

    while (Date.now() - startedAt < timeout) {
      if (signal?.aborted) return { refreshed: false, reason: "stopped_manually" };
      if (hasSnapshotChanged(dependentSelect, before)) {
        trace(options, `${labelFor(options.dependentFieldKey)} dropdown refreshed`);
        return { refreshed: true, reason: "" };
      }
      if (!dependentSelect.disabled && validOptions(dependentSelect).length > 0 && before.disabled) {
        trace(options, `${labelFor(options.dependentFieldKey)} dropdown refreshed`);
        return { refreshed: true, reason: "" };
      }
      await wait(160, signal);
    }

    return { refreshed: false, reason: "dropdown_not_loaded" };
  }

  async function choose(select, options = {}) {
    const fieldKey = options.fieldKey || "";
    const fieldLabel = labelFor(fieldKey);
    const attempts = Math.max(1, Math.min(Number(options.attempts || MAX_RETRIES), MAX_RETRIES));
    const timeout = Math.max(400, Math.min(Number(options.timeout || MAX_WAIT_PER_FIELD), MAX_WAIT_PER_FIELD));
    const startedAt = Date.now();
    let lastReason = "option_not_found";

    console.log("[FIVA]", `available ${fieldKey || "dropdown"} options:`, availableOptionLabels(select));

    for (let attempt = 0; attempt < attempts; attempt += 1) {
      if (options.signal?.aborted) return { success: false, reason: "stopped_manually", attempts: attempt };
      const remaining = timeout - (Date.now() - startedAt);
      if (remaining <= 0) break;

      trace(options, `${fieldLabel} dropdown opened`);
      const ready = await waitForDropdownReady(select, {
        ...options,
        timeout: remaining,
        quiet: 500
      });
      if (!ready.ready) {
        lastReason = ready.reason || "dropdown_not_loaded";
        break;
      }

      console.log("[FIVA]", `available ${fieldKey || "dropdown"} options:`, availableOptionLabels(select));
      const matched = matchOption(select, options);
      if (!matched.option && fieldKey === "mission") {
        const dhakaOption = validOptions(select).find((option) => /DHAKA/i.test(`${optionText(option)} ${optionValue(option)}`));
        if (dhakaOption) matched.option = dhakaOption;
      }
      if (!matched.option) {
        lastReason = "option_not_found";
        trace(options, `${fieldLabel} option not found`, "warn");
        await wait(180, options.signal);
        continue;
      }

      const dependentSelect = typeof options.getDependentSelect === "function" ? options.getDependentSelect() : null;
      const dependentBefore = snapshot(dependentSelect);
      trace(options, `Matched option: ${optionText(matched.option) || optionValue(matched.option)}`);
      await applyHumanSelection(select, matched.option, options);

      const verified = typeof options.verify === "function"
        ? options.verify(select, matched.option)
        : verifySelection(select, matched.option, options);
      if (!verified || !verifySelection(select, matched.option, options)) {
        lastReason = "verification_failed";
        trace(options, `${fieldLabel} selection verification failed`, "warn");
        await wait(220, options.signal);
        continue;
      }

      trace(options, `${fieldLabel} selected: ${optionText(matched.option) || optionValue(matched.option)}`);
      if (dependentSelect) {
        const refresh = await waitForAspNetUpdate(dependentSelect, dependentBefore, {
          ...options,
          timeout: Math.min(8000, Math.max(1200, timeout - (Date.now() - startedAt)))
        });
        if (!refresh.refreshed) trace(options, `${labelFor(options.dependentFieldKey)} dropdown refresh not detected`, "warn");
      }

      await humanDelay(STABILIZE_MIN_MS, STABILIZE_MAX_MS, options.signal);
      return {
        success: true,
        reason: "",
        option: matched.option,
        strategy: matched.strategy,
        attempts: attempt + 1,
        optionCount: matched.optionCount,
        selectedText: optionText(matched.option),
        selectedValue: optionValue(matched.option)
      };
    }

    console.log("[FIVA]", `available ${fieldKey || "dropdown"} options:`, availableOptionLabels(select));
    return {
      success: false,
      reason: lastReason,
      attempts,
      optionCount: validOptions(select).length
    };
  }

  const HumanSelectOption = Object.freeze({
    MAX_RETRIES,
    MAX_WAIT_PER_FIELD,
    STABILIZE_MIN_MS,
    STABILIZE_MAX_MS,
    applyHumanSelection,
    availableOptionLabels,
    choose,
    compactMatch,
    humanOpen,
    isPlaceholderOption,
    matchOption,
    normalize,
    snapshot,
    validOptions,
    verifySelection,
    waitForAspNetUpdate,
    waitForDropdownReady
  });

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

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