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

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

  const CONTROL_CHARS = /[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F-\u009F\u00A0\u1680\u180E\u2000-\u200F\u2028-\u202F\u205F\u2060\u3000\uFEFF]/g;
  const GARBAGE_LINE = /^[^A-Z0-9@<]{4,}$/i;
  const DASHES = /[‐‑‒–—―−]/g;

  function compact(value = "") {
    return String(value || "")
      .normalize("NFKC")
      .replace(CONTROL_CHARS, " ")
      .replace(DASHES, "-")
      .replace(/[“”]/g, "\"")
      .replace(/[‘’]/g, "'")
      .replace(/\s+/g, " ")
      .trim();
  }

  function cleanOcrArtifacts(value = "") {
    const normalizer = global.FIVA?.NormalizeOptionText;
    if (normalizer?.cleanOcrArtifacts) return normalizer.cleanOcrArtifacts(value);
    return compact(value)
      .replace(/\bB\s*A\s*N\s*G\s*L\s*A\s*D\s*E\s*S\s*H\b/gi, "BANGLADESH")
      .replace(/\bBAN\s+GLA\s+DESH\b/gi, "BANGLADESH")
      .replace(/\bBANG\s+LA\s+DESH\b/gi, "BANGLADESH")
      .replace(/\bBANGLA\s+DESH\b/gi, "BANGLADESH")
      .replace(/\bBANGLADES\s+H\b/gi, "BANGLADESH")
      .replace(/\bBANGLAD\s+ESH\b/gi, "BANGLADESH")
      .replace(/\bB\s*G\s*D\b/gi, "BGD")
      .replace(/\s+/g, " ")
      .trim();
  }

  function cleanLine(line = "") {
    return cleanOcrArtifacts(line)
      .replace(/\s*[:：]\s*/g, ": ")
      .replace(/\s*-\s*/g, "-")
      .replace(/\bE\s*-\s*mail\b/gi, "Email")
      .replace(/\bP\s*assport\b/gi, "Passport")
      .replace(/\bN\s*ationality\b/gi, "Nationality")
      .trim();
  }

  function cleanOCRText(text = "") {
    const rawLines = String(text || "")
      .replace(/\r/g, "\n")
      .split("\n")
      .map(cleanLine)
      .filter(Boolean)
      .filter((line) => !GARBAGE_LINE.test(line));

    const lines = [];
    rawLines.forEach((line) => {
      const previous = lines[lines.length - 1] || "";
      const joinsBrokenLabel = /(?:date of|passport|expected date of|given|indian)$/i.test(previous)
        && /^[a-z][a-z\s/-]{2,}$/i.test(line);
      if (joinsBrokenLabel) {
        lines[lines.length - 1] = `${previous} ${line}`.replace(/\s+/g, " ").trim();
        return;
      }
      lines.push(line);
    });

    return lines.join("\n").trim();
  }

  function normalizeExtractedText(text = "") {
    return cleanOCRText(text);
  }

  const CleanOCRText = Object.freeze({
    cleanLine,
    cleanOcrArtifacts,
    cleanOCRText,
    compact,
    normalizeExtractedText
  });

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

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