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

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

  const MAX_RETRIES = 3;
  const MAX_WAIT_PER_FIELD = 8000;
  const PLACEHOLDER_PATTERN = /\b(select|choose|loading|wait|please wait|please select|none)\b/i;
  const OPTION_CACHE = new WeakMap();

  function optionNormalizer() {
    return global.FIVA?.NormalizeOptionText || 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 labelFor(fieldKey = "") {
    return String(fieldKey || "dropdown")
      .replace(/([A-Z])/g, " $1")
      .replace(/^./, (letter) => letter.toUpperCase())
      .trim();
  }

  function normalize(value = "", fieldKey = "") {
    const normalizer = optionNormalizer();
    if (normalizer?.normalizeVisaValue) return normalizer.normalizeVisaValue(value, fieldKey);
    if (normalizer?.normalizeOptionText) return normalizer.normalizeOptionText(value, fieldKey);
    return String(value || "")
      .trim()
      .toUpperCase()
      .replace(/[_-]+/g, " ")
      .replace(/\s+/g, " ")
      .trim();
  }

  function compactMatch(value = "", fieldKey = "") {
    const normalizer = optionNormalizer();
    if (normalizer?.compactOptionText) return normalizer.compactOptionText(value, fieldKey);
    return normalize(value, fieldKey).replace(/\s+/g, "");
  }

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

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

  function isPlaceholderOption(option) {
    const normalizer = optionNormalizer();
    if (normalizer?.isPlaceholderOption) return normalizer.isPlaceholderOption(option);
    const text = optionText(option);
    const value = optionValue(option);
    if (!value && !text) return true;
    return PLACEHOLDER_PATTERN.test(text) || (!text && PLACEHOLDER_PATTERN.test(value));
  }

  function validOptions(select) {
    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 optionCacheSignature(select) {
    return Array.from(select?.options || [])
      .map((option) => `${option.index}:${optionValue(option)}:${optionText(option)}`)
      .join("|");
  }

  function normalizedOptions(select, fieldKey = "") {
    if (!select || typeof select !== "object") return [];
    const signature = optionCacheSignature(select);
    const cached = OPTION_CACHE.get(select);
    if (cached?.signature === signature && cached?.fieldKey === fieldKey) return cached.options;

    const options = validOptions(select).map((option) => {
      const raw = optionText(option) || optionValue(option);
      const rawValue = optionValue(option);
      const normalizer = optionNormalizer();
      const aliasText = normalizer?.nationalityAliasResolver?.(raw) || "";
      const aliasValue = normalizer?.nationalityAliasResolver?.(rawValue) || "";
      return {
        alias: aliasText || aliasValue,
        compact: compactMatch(raw, fieldKey),
        compactValue: compactMatch(rawValue, fieldKey),
        element: option,
        normalized: normalize(raw, fieldKey),
        normalizedValue: normalize(rawValue, fieldKey),
        raw,
        value: rawValue
      };
    });
    OPTION_CACHE.set(select, { fieldKey, options, signature });
    return options;
  }

  function candidatesFor(fieldKey = "", value = "") {
    const normalized = normalize(value, fieldKey);
    const alias = optionNormalizer()?.nationalityAliasResolver?.(value) || "";
    const candidates = new Set([normalized]);
    if (alias) candidates.add(alias);

    if (/BANGLADESH|BANGLADESHI/.test(normalized)) {
      candidates.add("BANGLADESH");
      candidates.add("BANGLADESHI");
      candidates.add("BD");
    }

    if (fieldKey === "mission" && /DHAKA|BANGLADESH/.test(normalized)) {
      candidates.add("BANGLADESH DHAKA");
      candidates.add("BANGLADESHDHAKA");
      candidates.add("DHAKA");
      candidates.add("INDIAN MISSION DHAKA");
    }

    if (fieldKey === "visaType" && /TOURIST|TOURISM|TRAVEL/.test(normalized)) {
      candidates.add("TOURIST");
      candidates.add("TOURIST VISA");
      candidates.add("E TOURIST VISA");
    }

    if (fieldKey === "visaType" && /BUSINESS/.test(normalized)) {
      candidates.add("BUSINESS");
      candidates.add("BUSINESS VISA");
      candidates.add("E BUSINESS VISA");
    }

    if (fieldKey === "visaType" && /MEDICAL/.test(normalized)) {
      candidates.add("MEDICAL");
      candidates.add("MEDICAL VISA");
      candidates.add("E MEDICAL VISA");
    }

    if (fieldKey === "purpose" && /TOURIST|TOURISM|TRAVEL/.test(normalized)) {
      candidates.add("TOURISM");
      candidates.add("TOURIST");
    }

    if (fieldKey === "purpose" && /MEDICAL/.test(normalized)) {
      candidates.add("MEDICAL");
      candidates.add("MEDICAL TREATMENT");
      candidates.add("MEDICAL ATTENDANT");
      candidates.add("FOR FOREIGN NATIONALS COMING AS MEDICAL ATTENDANTS");
    }

    return Array.from(candidates).filter(Boolean);
  }

  function levenshteinRatio(left = "", right = "") {
    const a = compactMatch(left).slice(0, 48);
    const b = compactMatch(right).slice(0, 48);
    if (!a || !b) return 0;
    const rows = Array.from({ length: a.length + 1 }, () => []);
    for (let i = 0; i <= a.length; i += 1) rows[i][0] = i;
    for (let j = 0; j <= b.length; j += 1) rows[0][j] = j;
    for (let i = 1; i <= a.length; i += 1) {
      for (let j = 1; j <= b.length; j += 1) {
        rows[i][j] = Math.min(
          rows[i - 1][j] + 1,
          rows[i][j - 1] + 1,
          rows[i - 1][j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1)
        );
      }
    }
    return 1 - (rows[a.length][b.length] / Math.max(a.length, b.length));
  }

  function safeFuzzyMatch(fieldKey = "", optionEntry, candidate = "") {
    const ratio = Math.max(
      levenshteinRatio(optionEntry.raw, candidate),
      levenshteinRatio(optionEntry.value, candidate)
    );
    if (fieldKey === "country" || fieldKey === "nationality") {
      const left = optionEntry.compact || optionEntry.compactValue;
      const right = compactMatch(candidate, fieldKey);
      if (left.length < 6 || right.length < 6) return false;
      if (left.slice(0, 4) !== right.slice(0, 4)) return false;
      return ratio >= 0.9;
    }
    return ratio >= 0.72;
  }

  function debugOptionMatch(fieldKey = "", value = "", entries = [], matched = null) {
    if (fieldKey !== "nationality") return;
    const normalizedValue = normalize(value, fieldKey);
    console.log("[FIVA]", `Nationality parser value: ${value || ""}`);
    console.log("[FIVA]", `Normalized value: ${normalizedValue}`);
    console.log("[FIVA]", "Available dropdown options:");
    entries.slice(0, 160).forEach((entry) => console.log("[FIVA]", `- ${entry.normalized || entry.raw}`));
    if (matched) console.log("[FIVA]", `Matched option: ${matched.normalized || matched.raw}`);
  }

  function matchOption(select, options = {}) {
    const fieldKey = options.fieldKey || "";
    const candidates = candidatesFor(fieldKey, options.value || "");
    const list = normalizedOptions(select, fieldKey);
    const strategies = [
      ["exact_normalized", (entry, candidate) => entry.normalized === normalize(candidate, fieldKey) || entry.normalizedValue === normalize(candidate, fieldKey)],
      ["alias", (entry, candidate) => {
        const alias = optionNormalizer()?.nationalityAliasResolver?.(candidate) || "";
        return Boolean(alias && (entry.alias === alias || entry.normalized === alias || entry.normalizedValue === alias));
      }],
      ["compact_normalized", (entry, candidate) => entry.compact === compactMatch(candidate, fieldKey) || entry.compactValue === compactMatch(candidate, fieldKey)],
      ["contains", (entry, candidate) => {
        const normalizer = optionNormalizer();
        if (normalizer?.matchesOptionValue?.(entry.element, candidate)) return true;
        const needle = compactMatch(candidate, fieldKey);
        if (needle.length < 3) return false;
        const parts = [entry.compactValue, entry.compact]
          .filter((part) => part.length >= 2);
        return parts.some((part) => part.includes(needle))
          || parts.some((part) => needle.includes(part));
      }],
      ["ocr_safe_fuzzy", (entry, candidate) => safeFuzzyMatch(fieldKey, entry, candidate)]
    ];

    for (const [strategy, matcher] of strategies) {
      for (const candidate of candidates) {
        const entry = list.find((item) => matcher(item, candidate));
        if (entry) {
          debugOptionMatch(fieldKey, options.value || "", list, entry);
          return { option: entry.element, strategy, candidates, optionCount: list.length };
        }
      }
    }

    debugOptionMatch(fieldKey, options.value || "", list, null);
    return { option: null, strategy: "", candidates, optionCount: list.length };
  }

  function optionSignature(select) {
    return validOptions(select)
      .map((option) => `${option.index}:${optionValue(option)}:${optionText(option)}`)
      .join("|");
  }

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

  async function waitForRealOptions(select, options = {}) {
    const timeout = Math.max(400, Math.min(Number(options.timeout || MAX_WAIT_PER_FIELD), MAX_WAIT_PER_FIELD));
    const quiet = Math.max(80, Number(options.quiet || 180));
    const signal = options.signal;
    const startedAt = Date.now();
    const fieldLabel = labelFor(options.fieldKey);
    let lastSignature = optionSignature(select);
    let stableSince = Date.now();
    let lastCount = validOptions(select).length;
    let loggedLoading = false;

    const observer = new MutationObserver(() => {
      lastSignature = optionSignature(select);
      stableSince = Date.now();
    });

    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 (!loggedLoading) {
          trace(options, `Loading ${fieldLabel.toLowerCase()} options`);
          loggedLoading = true;
        }

        if (select.disabled) {
          await wait(120, signal);
          continue;
        }

        const signature = optionSignature(select);
        if (signature !== lastSignature) {
          lastSignature = signature;
          stableSince = Date.now();
        }

        const list = validOptions(select);
        if (list.length !== lastCount) {
          lastCount = list.length;
          stableSince = Date.now();
        }
        if (list.length > 0 && Date.now() - stableSince >= quiet) {
          trace(options, `Found ${list.length} ${fieldLabel.toLowerCase()} options`);
          return { ready: true, reason: "", count: list.length };
        }

        await wait(120, signal);
      }

      const count = validOptions(select).length;
      return { ready: count > 0, reason: count > 0 ? "" : "dropdown_not_loaded", count };
    } finally {
      observer.disconnect();
    }
  }

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

  function nativeSetter(select, value) {
    const descriptor = Object.getOwnPropertyDescriptor(HTMLSelectElement.prototype, "value");
    if (descriptor?.set) descriptor.set.call(select, value);
    else select.value = value;
  }

  async function applyOption(select, option, options = {}) {
    if (!select || !option || options.signal?.aborted) return false;
    select.scrollIntoView({ block: "center", inline: "nearest" });
    select.dispatchEvent(new Event("focus", { bubbles: true, cancelable: true }));
    select.focus({ preventScroll: true });
    select.click();
    await wait(30 + Math.floor(Math.random() * 51), options.signal);
    if (options.signal?.aborted) return false;

    select.selectedIndex = option.index;
    option.selected = true;
    nativeSetter(select, option.value);
    fire(select, "input");
    fire(select, "change");
    await wait(30 + Math.floor(Math.random() * 51), options.signal);
    select.blur();
    fire(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;
    const target = options.value || "";
    const normalizer = optionNormalizer();
    if (target && normalizer?.matchesOptionValue && !normalizer.matchesOptionValue(selected, target)) 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 choose(select, options = {}) {
    const fieldLabel = labelFor(options.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 ${String(options.fieldKey || "dropdown").toLowerCase()} options:`, availableOptionLabels(select));

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

      const waitResult = await waitForRealOptions(select, {
        ...options,
        timeout: Math.max(400, timeout - elapsed)
      });
      if (!waitResult.ready) {
        lastReason = waitResult.reason || "dropdown_not_loaded";
        break;
      }

      console.log("[FIVA]", `available ${String(options.fieldKey || "dropdown").toLowerCase()} options:`, availableOptionLabels(select));
      const matched = matchOption(select, options);
      if (!matched.option) {
        lastReason = "option_not_found";
        trace(options, `${fieldLabel} option not found`, "warn");
        await wait(160, options.signal);
        continue;
      }

      trace(options, `Matched option: ${optionText(matched.option) || optionValue(matched.option)}`);
      await applyOption(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)) {
        trace(options, `${fieldLabel} selected successfully`);
        return {
          success: true,
          reason: "",
          option: matched.option,
          strategy: matched.strategy,
          attempts: attempt + 1,
          optionCount: matched.optionCount,
          selectedText: optionText(matched.option),
          selectedValue: optionValue(matched.option)
        };
      }

      lastReason = "verification_failed";
      trace(options, `${fieldLabel} selection verification failed`, "warn");
      await wait(180, options.signal);
    }

    return {
      success: false,
      reason: lastReason,
      attempts,
      optionCount: validOptions(select).length
    };
  }

  const SelectSmartOption = Object.freeze({
    MAX_RETRIES,
    MAX_WAIT_PER_FIELD,
    applyOption,
    availableOptionLabels,
    choose,
    compactMatch,
    candidatesFor,
    isPlaceholderOption,
    matchOption,
    normalize,
    optionText,
    optionValue,
    validOptions,
    verifySelection,
    waitForRealOptions
  });

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

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