// Lightweight vanilla form validation with Tailwind-friendly classes.
(function () {
  const FORM_SELECTOR = "form[data-parsley-validate], form[data-validate]";
  const ERROR_CLASS = "flex items-center text-rose-500 text-xs mt-1";
  const INVALID_CLASS = ["border-rose-500", "ring-2", "ring-rose-500/20"];

  function isHidden(el) {
    return el.type === "hidden" || el.offsetParent === null;
  }

  function getTarget(field) {
    if (field.tagName === "SELECT" && field.classList.contains("tw-selectpicker")) {
      return field.nextElementSibling || field;
    }
    return field;
  }

  function getValue(field) {
    if (field.type === "checkbox" || field.type === "radio") {
      const group = field.name ? field.form.querySelectorAll(`input[name="${field.name}"]`) : [field];
      const checked = Array.from(group).some((el) => el.checked);
      return checked ? "1" : "";
    }
    if (field.tagName === "SELECT" && field.multiple) {
      return Array.from(field.options).filter((o) => o.selected).map((o) => o.value).join(",");
    }
    return (field.value || "").trim();
  }

  function setInvalid(field, message) {
    const target = getTarget(field);
    target.classList.add(...INVALID_CLASS);
    field.setAttribute("aria-invalid", "true");

    let error = target.parentNode.querySelector(`[data-error-for="${field.name}"]`);
		
    if (!error) {
      error = document.createElement("div");
      error.className = ERROR_CLASS;
      error.dataset.errorFor = field.name;
      const icon = document.createElement("span");
      icon.className = "inline-flex items-center justify-center mr-1.5";
      icon.innerHTML =
        '<svg class="h-3.5 w-3.5 text-rose-500" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-8-4a1 1 0 00-1 1v4a1 1 0 102 0V7a1 1 0 00-1-1zm0 8a1.25 1.25 0 100-2.5A1.25 1.25 0 0010 14z" clip-rule="evenodd"/></svg>';
      const text = document.createElement("span");
      text.dataset.errorText = "true";
      error.appendChild(icon);
      error.appendChild(text);
      target.parentNode.appendChild(error);
    }
    const textNode = error.querySelector("[data-error-text]");
    if (textNode) {
      textNode.textContent = message;
    } else {
      error.textContent = message;
    }
  }

  function clearInvalid(field) {
    const target = getTarget(field);
    target.classList.remove(...INVALID_CLASS);
    field.removeAttribute("aria-invalid");
    const error = target.parentNode.querySelector(`[data-error-for="${field.name}"]`);
    if (error) error.remove();
  }

  function messageFor(field, key, fallback) {
    return field.getAttribute(`data-msg-${key}`) || field.getAttribute("data-msg") || fallback;
  }

  function validateField(field) {
    if (!field.name || field.disabled || field.hasAttribute("data-validate-skip")) return true;
    if (field.type === "hidden" && field.tagName !== "SELECT") return true;

    const value = getValue(field);
    const required = field.required || field.getAttribute("data-required") === "true";

    if (required && !value) {
      setInvalid(field, messageFor(field, "required", "This field is required."));
      return false;
    }

    if (!value) {
      clearInvalid(field);
      return true;
    }

    const type = field.getAttribute("data-type") || field.type;
    if (type === "email") {
      const emailOk = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
      if (!emailOk) {
        setInvalid(field, messageFor(field, "email", "Please enter a valid email."));
        return false;
      }
    }

    const minLength = parseInt(field.getAttribute("minlength") || field.getAttribute("data-minlength") || "0", 10);
    if (minLength && value.length < minLength) {
      setInvalid(field, messageFor(field, "minlength", `Minimum length is ${minLength}.`));
      return false;
    }

    const maxLength = parseInt(field.getAttribute("maxlength") || field.getAttribute("data-maxlength") || "0", 10);
    if (maxLength && value.length > maxLength) {
      setInvalid(field, messageFor(field, "maxlength", `Maximum length is ${maxLength}.`));
      return false;
    }

    const pattern = field.getAttribute("pattern") || field.getAttribute("data-pattern");
    if (pattern) {
      const re = new RegExp(pattern);
      if (!re.test(value)) {
        setInvalid(field, messageFor(field, "pattern", "Invalid format."));
        return false;
      }
    }

    const min = field.getAttribute("min") || field.getAttribute("data-min");
    const max = field.getAttribute("max") || field.getAttribute("data-max");
    if ((type === "number" || min || max) && value) {
      const num = parseFloat(value);
      if (!Number.isNaN(num)) {
        if (min !== null && min !== "" && num < parseFloat(min)) {
          setInvalid(field, messageFor(field, "min", `Minimum value is ${min}.`));
          return false;
        }
        if (max !== null && max !== "" && num > parseFloat(max)) {
          setInvalid(field, messageFor(field, "max", `Maximum value is ${max}.`));
          return false;
        }
      }
    }

    const matchSel = field.getAttribute("data-match") || field.getAttribute("data-parsley-equalto");
    if (matchSel) {
      const other = field.form.querySelector(matchSel);
      if (other && value !== other.value) {
        setInvalid(field, messageFor(field, "equalto", "Values do not match."));
        return false;
      }
    }

    clearInvalid(field);
    return true;
  }

  function validateForm(form) {
    const fields = form.querySelectorAll("input, select, textarea");
    let ok = true;
    fields.forEach((field) => {
      if (!validateField(field)) ok = false;
    });
    if (!ok) {
      const first = form.querySelector('[aria-invalid="true"]');
      if (first && !isHidden(first)) first.scrollIntoView({ behavior: "smooth", block: "center" });
    }
    return ok;
  }

  function bind(form) {
    if (form.dataset.validationBound === "true") return;
    form.dataset.validationBound = "true";

    // Disable native HTML5 validation bubbles.
    form.noValidate = true;

    form.addEventListener("submit", (e) => {
      if (!validateForm(form)) {
        e.preventDefault();
        e.stopPropagation();
      }
    });

    form.querySelectorAll("input, select, textarea").forEach((field) => {
      const handler = () => validateField(field);
      field.addEventListener("blur", handler);
      field.addEventListener("change", handler);
      field.addEventListener("input", handler);
    });
  }

  function init() {
    document.querySelectorAll(FORM_SELECTOR).forEach(bind);
  }

  window.FormValidator = { init, validateForm, validateField };

  if (document.readyState === "interactive" || document.readyState === "complete") {
    init();
  } else {
    document.addEventListener("DOMContentLoaded", init);
  }
})();
