(function ($) {

  $(document).ready(function () {
    // ----------------- Global Settings -----------------
    let currencySymbol = "$"; // Change to any currency symbol dynamically
    let decimalPlaces = 2; // Adjust between 2 or 3 dynamically

    // ----------------- Formatting Functions -----------------
    function formatString(digits, pattern) {


      let formatted = "";
      let index = 0;
      for (let char of pattern) {
        if (char === "#") {
          if (index < digits.length) {
            formatted += digits[index++];
          } else {
            break;
          }
        } else {
          formatted += char;
        }
      }
      

      return formatted;
    }

    // ----------------- Money Mask Formatting -----------------
    function formatMoneyInput(value, type, prefix, input) {
      let decimalSeparator = type === "C" ? "." : ",";
      let thousandSeparator = type === "C" ? "," : ".";
      let rawDigits = value.replace(/\D/g, "");
      decimalPlaces = 2;
      if (input !== "") {
        decimalPlaces = Number(input.dataset.decimalPlaces);
      }

      if (rawDigits.length === 0) {
        return `${prefix}0${decimalSeparator}${"0".repeat(decimalPlaces)}`;
      }

      while (rawDigits.length < decimalPlaces + 1) {
        rawDigits = "0" + rawDigits;
      }

      let cents = rawDigits.slice(-decimalPlaces);
      let wholeNumber =
        rawDigits.slice(0, -decimalPlaces).replace(/^0+/, "") || "0";

      wholeNumber = wholeNumber.replace(
        /\B(?=(\d{3})+(?!\d))/g,
        thousandSeparator
      );
      return `${prefix}${wholeNumber}${decimalSeparator}${cents}`;
    }

    function handleMoneyInput(event) {
      let input = event.target;
      let oldValue = input.value;
      let oldCursorPos = input.selectionStart;

      let moneyPrefix = input.dataset.moneymaskPrefix;
      let moneymaskFormat = input.dataset.moneymaskFormat;
      let type = moneymaskFormat === "dot" ? "D" : "C";

      let newValue = '';

      if(input.value != '$0,00'){

        newValue = formatMoneyInput(input.value, type, moneyPrefix, input);
      }else{
        newValue = "";
      }



      if (input.value === newValue) return;

      input.value = newValue;
      let newCursorPos = oldCursorPos + (newValue.length - oldValue.length);
      setCaretPosition(input, newCursorPos);
    }

    // ----------------- Handling Focus & Blur for Money Mask -----------------
    $(document).on("focus", ".mask-moneyc", function () {


      let moneymaskFormat = $(this)[0].dataset.moneymaskFormat;
      let type = moneymaskFormat === "dot" ? "D" : "C";
      let decimalSeparator = type === "C" ? "." : ",";
      let baseFormat = `${currencySymbol}0${decimalSeparator}${"0".repeat(
        decimalPlaces
      )}`;
      

      if ($(this).val().trim() === "") {
        $(this).val(baseFormat);
        // setCaretPosition(this, $(this).val().length - (decimalPlaces + 1));
      }
    });

    $(document).on("blur", ".mask-moneyc", function () {
      let moneymaskFormat = $(this)[0].dataset.moneymaskFormat;
      let type = moneymaskFormat === "dot" ? "D" : "C";
      let decimalSeparator = type === "C" ? "." : ",";
      let baseFormat = `${currencySymbol}0${decimalSeparator}${"0".repeat(
        decimalPlaces
      )}`;

      let val = $(this).val().trim();

      let numericValue = val
        .replace(new RegExp(`[^0-9${decimalSeparator}]`, "g"), "")
        .replace(decimalSeparator, ".");

      if (parseFloat(numericValue) === 0 || val === baseFormat) {
        $(this).val("");
      }
    });

    // ----------------- Formatting Credit Card -----------------
    function formatCreditCard(digits, formatType) {
      let cardType = detectCardType(digits);

      if (cardType === "American Express") {
        digits = digits.slice(0, 15);
        return formatType === "space"
          ? formatString(digits, "#### ###### #####")
          : formatString(digits, "####-######-#####");
      } else {
        digits = digits.slice(0, 16);
        return formatType === "space"
          ? formatString(digits, "#### #### #### ####")
          : formatString(digits, "####-####-####-####");
      }
    }

    // ----------------- Card Logos -----------------
    const cardLogos = {
      Visa: fmeData.pluginUrl + "assets/svg-icons/visa-logo.svg",
      MasterCard: fmeData.pluginUrl + "assets/svg-icons/mastercard-logo.svg",
      "American Express": fmeData.pluginUrl + "assets/svg-icons/amex-logo.svg",
      Discover: fmeData.pluginUrl + "assets/svg-icons/discover-logo.svg",
      JCB: fmeData.pluginUrl + "assets/svg-icons/jcb-logo.svg",
      "Diners Club": fmeData.pluginUrl + "assets/svg-icons/cc-logo.svg",
      Maestro: fmeData.pluginUrl + "assets/svg-icons/maestro-logo.svg",
      UnionPay: fmeData.pluginUrl + "assets/svg-icons/cc-logo.svg",
      RuPay: fmeData.pluginUrl + "assets/svg-icons/repay-logo.svg",
      Unknown: fmeData.pluginUrl + "assets/svg-icons/cc-logo.svg",
    };

    // ----------------- Function to Detect Card Type -----------------
    function detectCardType(number) {
      const cleaned = number.replace(/\D/g, ""); // Remove non-numeric characters

      const cardPatterns = {
        Visa: /^4/,
        MasterCard: /^5[1-5]/, // MasterCard starts with 51-55
        "American Express": /^3[47]/, // AmEx starts with 34 or 37
        Discover: /^6(?:011|5)/, // Discover starts with 6011 or 65
        JCB: /^(?:2131|1800|35)/, // JCB starts with 2131, 1800, or 35
        "Diners Club": /^3(?:0[0-5]|[689])/, // Diners Club starts with 300-305, 36, or 38-39
        UnionPay: /^(62|81)/, // UnionPay starts with 62 or 81
        RuPay: /^(60|65|81|82|508)/, // RuPay starts with 60, 65, 81, 82, or 508
        Maestro: /^(50|5[6-9]|6[0-9])/, // Maestro starts with 50, 56-59, 60-69
      };

      for (let card in cardPatterns) {
        if (cardPatterns[card].test(cleaned)) {
          return card;
        }
      }

      return "Unknown"; // Default if no match is found
    }

    // ----------------- Function to Update Card Logo Dynamically -----------------
    function updateCardLogo(inputSelector) {
      let input = $(inputSelector);
      let logo = input.siblings(".card-logo"); // Select logo next to input
      let cardNumber = input.val().replace(/\D/g, ""); // Remove non-digit characters

      if (cardNumber === "") {
        logo.hide(); // Hide the logo if the input is empty
      } else {
        let cardType = detectCardType(cardNumber);
        if (cardType in cardLogos) {
          logo.attr("src", cardLogos[cardType]).show();
        } else {
          logo.hide(); // Hide logo if card type is unknown
        }
      }
    }

    // ----------------- Universal Field Formatting -----------------
    const formatFunctions = {
      ".mask-cnpj": (digits) => formatString(digits, "##.###.###/####-##"),
      ".mask-cpf": (digits) => formatString(digits, "###.###.###-##"),
      ".mask-cep": (digits) => formatString(digits, "#####-###"),
      ".mask-phus": (digits) => formatString(digits, "(###) ###-####"),
      ".mask-ph8": (digits) => formatString(digits, "####-####"),
      ".mask-ddd8": (digits) => formatString(digits, "(##) ####-####"),
      ".mask-ddd9": (digits) => formatString(digits, "(##) #####-####"),
      ".mask-dmy": (digits) => formatString(digits, "##/##/####"),
      ".mask-mdy": (digits) => formatString(digits, "##/##/####"),
      ".mask-hms": (digits) => formatString(digits, "##:##:##"),
      ".mask-hm": (digits) => formatString(digits, "##:##"),
      ".mask-dmyhm": (digits) => formatString(digits, "##/##/#### ##:##"),
      ".mask-mdyhm": (digits) => formatString(digits, "##/##/#### ##:##"),
      ".mask-my": (digits) => formatString(digits, "##/####"),
      ".mask-ccs": (digits) => formatCreditCard(digits, "space"),
      ".mask-cch": (digits) => formatCreditCard(digits, "hyphen"),
      ".mask-ccmy": (digits) => formatString(digits, "##/##"),
      ".mask-ccmyy": (digits) => formatString(digits, "##/####"),
      ".mask-moneyc": (digits) => formatMoneyInput(digits, "C", "$", ""),
      ".mask-moneyd": (digits) => formatMoneyInput(digits, "D", "$", ""),
      ".mask-ipv4": (digits) => formatString(digits, "###.###.###.###"), // New IPv4 Masking
    };

    // Apply formatting dynamically for all fields, including money inputs
    Object.entries(formatFunctions).forEach(([selector, formatFunction]) => {
      $(document).on("input focus", selector, function (event) {
        var input = this;
        var oldCaret = getCaretPosition(input);

        // Handle money input separately
        if (
          $(input).hasClass("mask-moneyc") ||
          $(input).hasClass("mask-moneyd")
        ) {


          let type = $(input).hasClass("mask-moneyc") ? "C" : "D";
          handleMoneyInput(event);
          return;
        }

        // Standard digit-based formatting for other fields
        var rawDigits = input.value.replace(/\D/g, ""); // Remove non-digits
        var digitIndex = getDigitIndexFromCaret(input.value, oldCaret);
        var newVal = formatFunction(rawDigits); // Use predefined function for other fields
        var newCaret = mapDigitIndexToCaret(newVal, digitIndex);


        if(newVal != '('){

          input.value = newVal;
        }else{
          input.value = "";
        }


        setCaretPosition(input, newCaret || 0); // Keep caret in place

        // Update card logo dynamically for credit card fields
        if ($(input).hasClass("mask-ccs") || $(input).hasClass("mask-cch")) {
          updateCardLogo(input);
        }
      });
    });

    // ----------------- Helper Functions for Caret Management -----------------
    function getCaretPosition(input) {
      return input.selectionStart;
    }
    function getDigitIndexFromCaret(formattedStr, caretPos) {
      var count = 0;
      for (var i = 0; i < caretPos; i++) {
        if (/\d/.test(formattedStr.charAt(i))) {
          count++;
        }
      }
      return count;
    }
    function mapDigitIndexToCaret(formattedStr, digitIndex) {
      var count = 0;
      for (var i = 0; i < formattedStr.length; i++) {
        if (/\d/.test(formattedStr.charAt(i))) {
          if (count === digitIndex) {
            return i;
          }
          count++;
        }
      }
      return formattedStr.length;
    }
    function setCaretPosition(elem, pos) {
      if (elem.setSelectionRange) {
        elem.focus();
        elem.setSelectionRange(pos, pos);
      } else if (elem.createTextRange) {
        var range = elem.createTextRange();
        range.collapse(true);
        range.moveEnd("character", pos);
        range.moveStart("character", pos);
        range.select();
      }
    }

    // ----------------- Backspace Handling -----------------
    $(document).on("keydown", "input", function (e) {
      // jQuery(".e-form__buttons__wrapper__button[data-direction='next']").off("click");


      if (e.key === "Backspace") {
        var input = this;

        // Special handling for money inputs
        if (
          $(input).hasClass("mask-moneyc") ||
          $(input).hasClass("mask-moneyd")
        ) {
          let decimalSeparator = $(input).hasClass("mask-moneyc") ? "." : ",";
          let baseFormat = `${currencySymbol}0${decimalSeparator}${"0".repeat(
            decimalPlaces
          )}`;

          if (input.value === baseFormat) {
            e.preventDefault(); // Prevent deletion beyond the base format
            return;
          }

          // Handle money input dynamically and prevent default formatting conflict
          handleMoneyInput(e);
          return;
        }

        // Standard backspace handling for all other masked inputs
        if (input.selectionStart !== input.selectionEnd) {
          e.preventDefault();
          input.value = "";
          setCaretPosition(input, 0);
          return;
        }

        var caretPos = getCaretPosition(input);
        var digitIndex = getDigitIndexFromCaret(input.value, caretPos);
        if (digitIndex === 0) return;

        var rawDigits = input.value.replace(/\D/g, "");
        var newDigits =
          rawDigits.slice(0, digitIndex - 1) + rawDigits.slice(digitIndex);

        // Find matching class for format function
        let matchedClass = Object.keys(formatFunctions).find((cls) =>
          $(input).hasClass(cls.substring(1))
        );



        if (matchedClass) {
          var formatted = formatFunctions[matchedClass](newDigits);
          input.value = formatted;
          var newCaretPos = mapDigitIndexToCaret(formatted, digitIndex - 1);
          setCaretPosition(input, newCaretPos);
        }
      }
    });

    // ----------------- Error Validation -----------------
    function validateInput(
      selector,
      errorClass,
      validationFunction,
      errorMessage
    ) {
      $(document).on("blur", selector, function () {
        var input = $(this);
        
        if(input.hasClass("hide-fme-mask-input")){
          return;
        }

        var val = input.val();
        var errorElement = input
          .closest(".elementor-field-group")
          .find("." + errorClass);

        // Remove predefined symbol if it's the ONLY character left
        if (val.length === 1 && !/\d/.test(val)) {
          input.val("");
          errorElement.hide().text("");
          return;
        }

        // Show error message if validation fails
        if (val !== "" && !validationFunction(val)) {
          errorElement
            .text(errorMessage)
            .css("display", "flex")
            .hide()
            .fadeIn(200);
        } else {
          errorElement.fadeOut(100, function () {
            $(this).css("display", "none");
          });
        }
      });

      $(document).on("input", selector, function () {
        var input = $(this);

        nextbtnVisibility(errorClass, input, validationFunction);

        var errorElement = input
          .closest(".elementor-field-group")
          .find("." + errorClass);

        if (errorElement.is(":visible")) {
          var val = input.val();
          if (validationFunction(val)) {
            errorElement.fadeOut(100, function () {
              $(this).css("display", "none");
            });
          }
        }
      });
    }

    // Apply validation dynamically for all fields
    const validations = {
      ".mask-cnpj": {
        errorClass: "error-cnpj",
        validate: isValidCNPJ,
        msg: fmeData.errorMessages["mask-cnpj"],
      },
      ".mask-cpf": {
        errorClass: "error-cpf",
        validate: isValidCPF,
        msg: fmeData.errorMessages["mask-cpf"],
      },
      ".mask-cep": {
        errorClass: "error-cep",
        validate: isValidCEP,
        msg: fmeData.errorMessages["mask-cep"],
      },
      ".mask-phus": {
        errorClass: "error-phus",
        validate: isValidPhoneUSA,
        msg: fmeData.errorMessages["mask-phus"],
      },
      ".mask-ph8": {
        errorClass: "error-ph8",
        validate: isValidPhone8,
        msg: fmeData.errorMessages["mask-ph8"],
      },
      ".mask-ddd8": {
        errorClass: "error-ddd8",
        validate: isValidPhoneDDD8,
        msg: fmeData.errorMessages["mask-ddd8"],
      },
      ".mask-ddd9": {
        errorClass: "error-ddd9",
        validate: isValidPhoneDDD9,
        msg: fmeData.errorMessages["mask-ddd9"],
      },
      ".mask-dmy": {
        errorClass: "error-dmy",
        validate: isValidDateDMY,
        msg: fmeData.errorMessages["mask-dmy"],
      },
      ".mask-mdy": {
        errorClass: "error-mdy",
        validate: isValidDateMDY,
        msg: fmeData.errorMessages["mask-mdy"],
      },
      ".mask-hms": {
        errorClass: "error-hms",
        validate: isValidTimeHMS,
        msg: fmeData.errorMessages["mask-hms"],
      },
      ".mask-hm": {
        errorClass: "error-hm",
        validate: isValidTimeHM,
        msg: fmeData.errorMessages["mask-hm"],
      },
      ".mask-dmyhm": {
        errorClass: "error-dmyhm",
        validate: isValidDateDMYHM,
        msg: fmeData.errorMessages["mask-dmyhm"],
      },
      ".mask-mdyhm": {
        errorClass: "error-mdyhm",
        validate: isValidDateMDYHM,
        msg: fmeData.errorMessages["mask-mdyhm"],
      },
      ".mask-my": {
        errorClass: "error-my",
        validate: isValidDateMY,
        msg: fmeData.errorMessages["mask-my"],
      },
      ".mask-ccs": {
        errorClass: "error-ccs",
        validate: isValidCreditCard,
        msg: fmeData.errorMessages["mask-ccs"],
      },
      ".mask-cch": {
        errorClass: "error-cch",
        validate: isValidCreditCard,
        msg: fmeData.errorMessages["mask-cch"],
      },
      ".mask-ccmy": {
        errorClass: "error-ccmy",
        validate: isValidExpiryMMYY,
        msg: fmeData.errorMessages["mask-ccmy"],
      },
      ".mask-ccmyy": {
        errorClass: "error-ccmyy",
        validate: isValidExpiryMMYYYY,
        msg: fmeData.errorMessages["mask-ccmyy"],
      },
      ".mask-ipv4": {
        errorClass: "error-ipv4",
        validate: isValidIPv4,
        msg: fmeData.errorMessages["mask-ipv4"],
      },
    };

    for (const [selector, { errorClass, validate, msg }] of Object.entries(
      validations
    )) {
      validateInput(selector, errorClass, validate, msg);
    }

    // ----------------- Validation Functions -----------------
    function isValidPhoneUSA(phoneStr) {
      return /^\(\d{3}\) \d{3}-\d{4}$/.test(phoneStr);
    }
    function isValidPhone8(phoneStr) {
      return /^\d{4}-\d{4}$/.test(phoneStr);
    }
    function isValidPhoneDDD8(phoneStr) {
      return /^\(\d{2}\) \d{4}-\d{4}$/.test(phoneStr);
    }
    function isValidPhoneDDD9(phoneStr) {
      return /^\(\d{2}\) 9\d{4}-\d{4}$/.test(phoneStr);
    }

    // ----------------- Universal Date & Time Validation Function -----------------
    function isValidDateTime(value, format) {
      let regexPattern;
      let expectedParts;

      switch (format) {
        case "DMY": // dd/mm/yyyy
          regexPattern = /^(\d{2})\/(\d{2})\/(\d{4})$/;
          expectedParts = ["day", "month", "year"];
          break;
        case "MDY": // mm/dd/yyyy
          regexPattern = /^(\d{2})\/(\d{2})\/(\d{4})$/;
          expectedParts = ["month", "day", "year"];
          break;
        case "HMS": // hh:mm:ss
          regexPattern = /^(\d{2}):(\d{2}):(\d{2})$/;
          expectedParts = ["hour", "minute", "second"];
          break;
        case "HM": // hh:mm
          regexPattern = /^(\d{2}):(\d{2})$/;
          expectedParts = ["hour", "minute"];
          break;
        case "DMY-HM": // dd/mm/yyyy hh:mm
          regexPattern = /^(\d{2})\/(\d{2})\/(\d{4}) (\d{2}):(\d{2})$/;
          expectedParts = ["day", "month", "year", "hour", "minute"];
          break;
        case "MDY-HM": // mm/dd/yyyy hh:mm
          regexPattern = /^(\d{2})\/(\d{2})\/(\d{4}) (\d{2}):(\d{2})$/;
          expectedParts = ["month", "day", "year", "hour", "minute"];
          break;
        case "MY": // mm/yyyy
          regexPattern = /^(\d{2})\/(\d{4})$/;
          expectedParts = ["month", "year"];
          break;
        default:
          return false; // Unsupported format
      }

      // Match input with regex
      let match = value.match(regexPattern);
      if (!match) return false;

      // Extract values dynamically
      let parts = {};
      expectedParts.forEach((part, index) => {
        parts[part] = parseInt(match[index + 1], 10);
      });

      // Validate Date (Day, Month, Year)
      if (parts.year && (parts.year < 1500 || parts.year > 3000)) return false;
      if (parts.month && (parts.month < 1 || parts.month > 12)) return false;
      if (parts.day) {
        let daysInMonth = new Date(parts.year, parts.month, 0).getDate();
        if (parts.day < 1 || parts.day > daysInMonth) return false;
      }

      // Validate Time (Hour, Minute, Second)
      if (parts.hour && (parts.hour < 0 || parts.hour >= 24)) return false;
      if (parts.minute && (parts.minute < 0 || parts.minute >= 60))
        return false;
      if (parts.second && (parts.second < 0 || parts.second >= 60))
        return false;

      return true;
    }

    // ----------------- Expiry Date Validation -----------------
    function isValidExpiryDate(value, format) {
      let regexPattern =
        format === "MM/YY" ? /^(\d{2})\/(\d{2})$/ : /^(\d{2})\/(\d{4})$/;
      let match = value.match(regexPattern);

      if (!match) return false; // Invalid format

      let month = parseInt(match[1], 10);
      let year = parseInt(match[2], 10);

      let currentYear = new Date().getFullYear();
      let currentMonth = new Date().getMonth() + 1; // JS months are 0-based

      // Adjust year format if MM/YY (Assume 20XX for past/future dates)
      if (format === "MM/YY") {
        year += 2000;
      }

      // Validate month (01-12)
      if (month < 1 || month > 12) return false;

      // Validate expiry date (must be in the future)
      if (
        year < currentYear ||
        (year === currentYear && month < currentMonth)
      ) {
        return false;
      }

      return true;
    }

    // ----------------- Exportable Functions Using Universal Validator -----------------
    function isValidDateDMY(dateStr) {
      return isValidDateTime(dateStr, "DMY");
    }
    function isValidDateMDY(dateStr) {
      return isValidDateTime(dateStr, "MDY");
    }
    function isValidTimeHMS(timeStr) {
      return isValidDateTime(timeStr, "HMS");
    }
    function isValidTimeHM(timeStr) {
      return isValidDateTime(timeStr, "HM");
    }
    function isValidDateDMYHM(dateTimeStr) {
      return isValidDateTime(dateTimeStr, "DMY-HM");
    }
    function isValidDateMDYHM(dateTimeStr) {
      return isValidDateTime(dateTimeStr, "MDY-HM");
    }
    function isValidDateMY(dateStr) {
      return isValidDateTime(dateStr, "MY");
    }
    function isValidExpiryMMYY(dateStr) {
      return isValidExpiryDate(dateStr, "MM/YY");
    }
    function isValidExpiryMMYYYY(dateStr) {
      return isValidExpiryDate(dateStr, "MM/YYYY");
    }

    // Apply validation for credit card fields
    function isValidCreditCard(cardNumber) {
      const cleaned = cardNumber.replace(/\D/g, ""); // Remove non-digits
      if (cleaned.length < 15 || cleaned.length > 16) return false; // AMEX: 15, Others: 16
      let sum = 0;
      let shouldDouble = false;
      // Luhn Algorithm to validate card number
      for (let i = cleaned.length - 1; i >= 0; i--) {
        let digit = parseInt(cleaned.charAt(i), 10);

        if (shouldDouble) {
          digit *= 2;
          if (digit > 9) digit -= 9;
        }

        sum += digit;
        shouldDouble = !shouldDouble;
      }
      return sum % 10 === 0;
    }

    // Validate CNPJ (Brazilian Business ID - 14 digits)
    function isValidCNPJ(cnpj) {
      cnpj = cnpj.replace(/\D/g, ""); // Remove non-numeric characters
      if (cnpj.length !== 14) return false;

      // Eliminate obvious invalid CNPJs (repeated digits)
      if (/^(\d)\1+$/.test(cnpj)) return false;

      let calcCheckDigit = (cnpj, length) => {
        let weights =
          length === 12
            ? [5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2]
            : [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2];
        let sum = 0;

        for (let i = 0; i < weights.length; i++) {
          sum += parseInt(cnpj.charAt(i)) * weights[i];
        }

        let remainder = sum % 11;
        return remainder < 2 ? 0 : 11 - remainder;
      };

      // Validate both check digits
      let firstCheck = calcCheckDigit(cnpj, 12);
      let secondCheck = calcCheckDigit(cnpj, 13);

      return (
        firstCheck === parseInt(cnpj.charAt(12)) &&
        secondCheck === parseInt(cnpj.charAt(13))
      );
    }

    // Validate CPF (Brazilian Individual ID - 11 digits)
    function isValidCPF(cpf) {
      cpf = cpf.replace(/\D/g, ""); // Remove non-numeric characters
      if (cpf.length !== 11) return false;

      // Eliminate obvious invalid CPFs (repeated digits)
      if (/^(\d)\1+$/.test(cpf)) return false;

      let calculateDigit = (cpf, length) => {
        let sum = 0;
        for (let i = 0; i < length; i++) {
          sum += parseInt(cpf.charAt(i)) * (length + 1 - i);
        }
        let result = (sum * 10) % 11;
        return result === 10 ? 0 : result;
      };

      let firstDigit = calculateDigit(cpf, 9);
      let secondDigit = calculateDigit(cpf, 10); // Now correctly includes first digit in the calculation

      return (
        firstDigit === parseInt(cpf.charAt(9)) &&
        secondDigit === parseInt(cpf.charAt(10))
      );
    }

    // Validate CEP (Brazilian Postal Code - XXXXX-XXX)
    function isValidCEP(cep) {
      return /^\d{5}-\d{3}$/.test(cep); // Matches format XXXXX-XXX
    }

    // Validate IPv4 (Format: 192.168.001.001)
    function isValidIPv4(ip) {
      // Ensure correct format (###.###.###.###)
      const ipv4Pattern = /^(?:\d{1,3}\.){3}\d{1,3}$/;

      if (!ipv4Pattern.test(ip)) return false; // Check general format

      // Split by dots and validate each octet (0-255)
      const octets = ip.split(".");
      return octets.every((octet) => {
        let num = parseInt(octet, 10); // Convert to number
        return num >= 0 && num <= 255; // Each octet must be between 0-255
      });
    }

    // ----------------- Form Submit -----------------

    // start - step field mask error handling

    let maskErrorArr = {};
    let nextBtnOriginalClicks = {};
    let clickStatus={};
    let recaptchaEvent = {};
    let submitBtnEvent = {};


    const nextbtnVisibility = (errorClass, input, validationFunction) => {

      const closesWidget = input.closest(".elementor-widget-form");
      const widgetId = closesWidget.data('id');
      const inuptId = input.attr('id');
      const fieldStep=input.closest(".elementor-field-type-step");
      const form = input.closest(".elementor-form");

      const currectStepFields = form.find(".elementor-form-fields-wrapper.elementor-labels-above").children("div:not(.elementor-hidden)").find('input, textarea, select');

      const submtBtnTag = form.find("button[type='submit']");

      if (!closesWidget.length || !fieldStep.length) {
        return;
      }

      const nextBtn = fieldStep.find(".e-form__buttons__wrapper__button[data-direction='next']");

      

      if ((nextBtn.length == 0 && submtBtnTag.length > 0 ) && (submtBtnTag.length == 0 && nextBtn.length == 0) ) {
        return;
      }



      let val = input.val();

      if (val.length === 1 && !/\d/.test(val)) {
        val = '';
      }

      // Show error message if validation fails
      if (  val !== "" && !validationFunction(val)) {


        if (closesWidget.length > 0) {


          if (!maskErrorArr[widgetId]) {
            maskErrorArr[widgetId] = [];
          }

          if (!maskErrorArr[widgetId].includes(inuptId)) {
            maskErrorArr[widgetId].push(inuptId);
          }
        }

        if (!nextBtnOriginalClicks[widgetId] || !nextBtnOriginalClicks[widgetId].length) {



          if(nextBtn.length > 0){

            const origninalClicks = jQuery._data(nextBtn[0], "events");
  
            if (origninalClicks && (!nextBtnOriginalClicks[widgetId] || !nextBtnOriginalClicks[widgetId].length === 0)) {
              nextBtnOriginalClicks[widgetId] = origninalClicks && origninalClicks.click ? origninalClicks.click.map(h => h.handler) : [];
  
            }
          }


        }
      } else {
        if (maskErrorArr[widgetId] && maskErrorArr[widgetId].includes(inuptId)) {
          maskErrorArr[widgetId] = maskErrorArr[widgetId].filter(item => item !== inuptId);
        }
      }


      if (maskErrorArr[widgetId] && maskErrorArr[widgetId].length > 0) {

        if(nextBtn.length > 0){

          const origninalClicks = jQuery._data(nextBtn[0], "events");

        if (origninalClicks && (!nextBtnOriginalClicks[widgetId] || !nextBtnOriginalClicks[widgetId].length)) {


          nextBtnOriginalClicks[widgetId] = origninalClicks && origninalClicks.click ? origninalClicks.click.map(h => h.handler) : [];

        }
        
        if (nextBtnOriginalClicks[widgetId] && nextBtnOriginalClicks[widgetId].length > 0) {
          nextBtn.off("click");
        }


        }


        
      } else {


        // when no error in mask validation

        if(recaptchaEvent[widgetId]){
              submtBtnTag.on("click", recaptchaEvent[widgetId]);
        }

        if(submitBtnEvent[widgetId]){
          form.on("submit", submitBtnEvent[widgetId]);
        }



        if (nextBtnOriginalClicks[widgetId] && nextBtnOriginalClicks[widgetId].length > 0) {




          let isfieldsValid = true

          for (let i = 0; i < currectStepFields.length; i++) {


            if (currectStepFields[i].checkValidity() == false) {

              isfieldsValid = false;
              break;

            }

          }

          if (isfieldsValid) {





            if(nextBtn.length > 0){

              // 2️⃣ First, clear existing handlers to avoid duplication
              nextBtn.off("click");
              
              // Re-attach original click handlers only once
              nextBtnOriginalClicks[widgetId].forEach(fn => {
                nextBtn.one("click", fn); // use .one instead of .on
              });
            }


          }
            
          }

      }
    }


    

    // handle next button event before click

     $(document).on("mousedown", ".e-form__buttons__wrapper__button[data-direction='next']", function(e){

      const form = $(this).closest(".elementor-form");

      const mask_error_div = form.find(".elementor-form-fields-wrapper.elementor-labels-above").children("div:not(.elementor-hidden)").find('div.mask-error');

      const closesWidget = $(this).closest(".elementor-widget-form");
      const widgetId = closesWidget.data('id');

      let mask_error = false;

      // finding mask error
      for(let i=0; i<mask_error_div.length; i++){
        if(mask_error_div[i].value != "" && mask_error_div[i].style.display == 'flex'){
          mask_error = true;
          break;
        }
      }

      // reattcach next button event when no mask error

       if (maskErrorArr[widgetId] && !maskErrorArr[widgetId].length > 0 && !mask_error) {
          // Re-attach original click handlers only once
            
          if (nextBtnOriginalClicks[widgetId] && nextBtnOriginalClicks[widgetId].length > 0) {
              $(this).off("click");

              nextBtnOriginalClicks[widgetId].forEach(fn => {
                $(this).one("click", fn); // use .one instead of .on
              });
            }


       }

     })


     // handling next button event when previous button click

    $(document).on("click", ".e-form__buttons__wrapper__button[data-direction='previous']", function(e){

      const form = $(this).closest(".elementor-form");
      
      const closesWidget = form.closest(".elementor-widget-form");
      const widgetId = closesWidget.data('id');
      maskErrorArr[widgetId] = [];

      const currectStepFields = form.find(".elementor-form-fields-wrapper.elementor-labels-above").children("div:not(.elementor-hidden)")

      const nextBtn = currectStepFields.find(".e-form__buttons__wrapper__button[data-direction='next']");

      // attaching events to next button
      
      if (nextBtnOriginalClicks[widgetId] && nextBtnOriginalClicks[widgetId].length > 0) {
        nextBtn.off("click");

        nextBtnOriginalClicks[widgetId].forEach(fn => {
                nextBtn.one("click", fn); // use .one instead of .on
          });

      }

    })

     // handle mask validation on submit button of step field form
     $(document).on("mousedown", ".elementor-field-type-submit", function (e){

      var $submitBtn = $(this);

       var $form = $submitBtn.closest("form");

       const closesWidget = $form.closest(".elementor-widget-form");
      const widgetId = closesWidget.data('id');

      const currectStepFields = $form.find(".elementor-form-fields-wrapper.elementor-labels-above").children("div:not(.elementor-hidden)")

      const previousBtn = currectStepFields.find(".e-form__buttons__wrapper__button[data-direction='previous']");

      const inputMaskFields = currectStepFields.find("input.fme-mask-input");

      // run only first time when click on submit button 
      // if maskerror found in the form widget this will remove submit button and form events to prevent submit


      if(previousBtn && previousBtn.length && inputMaskFields && inputMaskFields.length && maskErrorArr[widgetId] && maskErrorArr[widgetId].length){

        var $subBtnTag = $submitBtn.find("button");

      
      // getting submit button recaptcha and click events

      var $form = $submitBtn.closest("form");

      const origninalclick = jQuery._data($subBtnTag[0], "events");


      if(origninalclick && origninalclick.click){

            origninalclick.click.forEach((ele) => {
            if(ele.handler.toString().trim().includes("onV3FormSubmit")){

              if(!recaptchaEvent[widgetId]){

                recaptchaEvent[widgetId] = ele.handler;

              }

            }
          })

      }

      // getting form apply step events

      const origninalSubmit = jQuery._data($form[0], "events");

      if(origninalSubmit && origninalSubmit.submit){

        origninalSubmit.submit.forEach((ele) => {
          if(ele.handler.toString().trim().includes("resetForm")){
  
            if(!submitBtnEvent[widgetId]){
  
              submitBtnEvent[widgetId] = ele.handler;
  
            }
  
          }
        })
      }

      if(submitBtnEvent[widgetId]){
          // removing form apply step event
          $form.off("submit", submitBtnEvent[widgetId]);
      }


      if(recaptchaEvent[widgetId]){
          // removing submit button click events
          $subBtnTag.off("click", recaptchaEvent[widgetId]);
      }


      }
    })

    // end - step field mask error handling


    // handling form submit of mask field in elementor form

    $(document).on("click", ".elementor-field-type-submit", function (e) {

      var $submitBtn = $(this);

      if ($submitBtn.find('button').hasClass('cfkef-prevent-submit') || $submitBtn.find('button').hasClass('confirmation-pending')
      ) {
        return
      }

      var $form = $submitBtn.closest("form");

      const closesWidget = $form.closest(".elementor-widget-form");
      const widgetId = closesWidget.data('id');
      const submtBtnTag = $form.find("button[type='submit']");

      // Prevent double-clicks
      if ($submitBtn.data("clicked")) {
        e.preventDefault();
        return;
      }
      $submitBtn.data("clicked", true); // Mark as clicked

      // Trigger blur on inputs to ensure validation runs
      $form.find("input").trigger("blur");

      // Add Elementor waiting class
      $form[0].classList.add("elementor-form-waiting");

      // Wait for mask errors or blur logic to complete
      setTimeout(() => {
        let hasVisibleMaskError = false;

        // Check for visible mask error messages
        const $errors = $form.find(".mask-error").filter(function () {
          return $(this).text().trim() !== "" && $(this).css("display") == "flex";
      });

      if ($errors.length > 0) {
        hasVisibleMaskError = true;
        const $firstError = $errors.first();
        $("html, body").animate({
          scrollTop: $firstError.offset().top - 200
        }, 300);
      }

      // ✅ Check for empty required masked fields
      const $emptyRequiredMasked = $form.find("input[required]").filter(function () {
        if(!$(this).hasClass('hide-fme-mask-input')){
          const val = $(this).val().trim();
          const isVisible = $(this).css("display") == "flex";
          return (val === "" || /^[\s_\-\(\)\.:/]+$/.test(val));
        }
      });

      if ($emptyRequiredMasked.length > 0) {
        hasVisibleMaskError = true;
        const $firstEmpty = $emptyRequiredMasked.first();
        $("html, body").animate({
          scrollTop: $firstEmpty.offset().top - 200
        }, 300);
        $firstEmpty.focus();
      }

      // ❌ Validation failed
      if (hasVisibleMaskError) {
        // $form[0].classList.remove("elementor-form-waiting");
        $submitBtn.data("clicked", false);
        e.preventDefault();
        return;
      }

      // ❌ Validation failed
      if (hasVisibleMaskError) {
        // $form[0].classList.remove("elementor-form-waiting");
        $submitBtn.data("clicked", false);
        e.preventDefault();
        return;
      }

      if (!hasVisibleMaskError) { 
        // ✅ All good — submit the form


        if(recaptchaEvent[widgetId]){
          submtBtnTag.on("click", recaptchaEvent[widgetId]);
          submtBtnTag.trigger("click");
        }

        if(submitBtnEvent[widgetId]){
          $form.on("submit", submitBtnEvent[widgetId]);
          submtBtnTag.trigger("click");

        }

        $form[0].classList.remove("elementor-form-waiting");
          $submitBtn.data("clicked", false);
          if(!recaptchaEvent[widgetId]){

            let error_messages = $form.find('.elementor-form-fields-wrapper').find('.elementor-message');

            if(error_messages && error_messages.length == 0){

              $form[0].requestSubmit();
            }


          }
      }

      }, 500);
    });
  });


})(jQuery);
