import { Effect } from "effect";
import { computed, inject, nextTick, onScopeDispose, provide, ref } from "vue";
import { getLocalizedText } from "../../../share/locale.js";
import { interpolate } from "../../../utils/interpolate.js";
import { useFormState } from "./state.js";
const VALIDATION_KEY = Symbol("shwfed-form-validation");
export function provideValidation(host) {
  const revealed = ref(false);
  const entries = /* @__PURE__ */ new Map();
  function union() {
    const out = [];
    for (const entry of entries.values()) out.push(...entry.results());
    return out;
  }
  const provider = {
    revealed,
    reveal: () => {
      revealed.value = true;
    },
    register: (fieldId, results) => {
      const token = Symbol("field");
      entries.set(token, { fieldId, results });
      return () => {
        entries.delete(token);
      };
    },
    async validate() {
      host.flush();
      await host.seeded;
      await host.settled();
      await nextTick();
      const results = union();
      provider.reveal();
      return results.every((r) => r.severity !== "error");
    },
    resultsFor(fieldIds) {
      const wanted = new Set(fieldIds);
      const out = [];
      for (const entry of entries.values()) {
        if (wanted.has(entry.fieldId())) out.push(...entry.results());
      }
      return out;
    }
  };
  provide(VALIDATION_KEY, provider);
  return provider;
}
export function useValidationProvider() {
  return inject(VALIDATION_KEY, void 0);
}
const DEFAULT_REQUIRED_MESSAGE = {
  zh: "\u8BF7\u586B\u5199\u6B64\u5B57\u6BB5",
  ja: "\u3053\u306E\u9805\u76EE\u3092\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044",
  en: "Please fill in this field",
  ko: "\uC774 \uD56D\uBAA9\uC744 \uC785\uB825\uD574 \uC8FC\uC138\uC694"
};
function defaultRequiredMessage(locale) {
  return DEFAULT_REQUIRED_MESSAGE[locale] ?? DEFAULT_REQUIRED_MESSAGE.zh;
}
function isEmptyValue(value) {
  return value == null || value === "" || Array.isArray(value) && value.length === 0;
}
export function useFieldValidation(options) {
  const { getAt, dirty } = useFormState();
  const provider = useValidationProvider();
  function evalBool(expression) {
    if (!expression) return false;
    try {
      return Effect.runSync(options.$cel(expression));
    } catch {
      return false;
    }
  }
  const isRequired = computed(() => evalBool(options.config().required));
  function resolveMessage(message) {
    const template = getLocalizedText(message, options.locale()) ?? "";
    if (!template) return "";
    return interpolate(template, (expression) => {
      try {
        const out = Effect.runSync(options.$cel(expression));
        return out == null ? "" : String(out);
      } catch {
        return "";
      }
    });
  }
  const results = computed(() => {
    const config = options.config();
    const out = [];
    if (typeof config.binding === "string" && isRequired.value && isEmptyValue(getAt(config.binding))) {
      out.push({ severity: "error", message: defaultRequiredMessage(options.locale()), ruleIndex: -1 });
    }
    const rules = config.validations ?? [];
    rules.forEach((rule, ruleIndex) => {
      if (!evalBool(rule.when)) return;
      const severity = rule.warning ? "warning" : "error";
      out.push({ severity, message: resolveMessage(rule.message), ruleIndex });
    });
    return out;
  });
  if (provider) {
    const unregister = provider.register(options.fieldId, () => results.value);
    onScopeDispose(unregister);
  }
  const errors = computed(() => results.value.filter((r) => r.severity === "error").map((r) => r.message));
  const warnings = computed(() => results.value.filter((r) => r.severity === "warning").map((r) => r.message));
  const visible = computed(() => {
    if (provider?.revealed.value) return true;
    const binding = options.config().binding;
    return typeof binding === "string" && dirty.value.has(binding);
  });
  return { isRequired, errors, warnings, visible };
}
