import { z } from "zod";
import { testLinearRegex } from "../../lib/regex/linear.js";
const MAX_CUSTOM_FIELD_REGEX_PATTERN_LENGTH = 500;
const MAX_CUSTOM_FIELD_REGEX_INPUT_LENGTH = 1e4;
const MAX_CUSTOM_FIELD_KEYS_PER_RECORD = 128;
const UNKNOWN_CUSTOM_FIELD_ERROR = "[internal] Unknown custom field";
const TOO_MANY_CUSTOM_FIELDS_ERROR = "[internal] Too many custom fields";
const VALIDATION_RULES = [
  "required",
  "date",
  "integer",
  "float",
  "lt",
  "lte",
  "gt",
  "gte",
  "eq",
  "ne",
  "regex"
];
const validationRuleSchema = z.discriminatedUnion("rule", [
  z.object({ rule: z.literal("required"), message: z.string().min(1) }),
  z.object({ rule: z.literal("date"), message: z.string().min(1) }),
  z.object({ rule: z.literal("integer"), message: z.string().min(1) }),
  z.object({ rule: z.literal("float"), message: z.string().min(1) }),
  z.object({ rule: z.literal("lt"), param: z.number(), message: z.string().min(1) }),
  z.object({ rule: z.literal("lte"), param: z.number(), message: z.string().min(1) }),
  z.object({ rule: z.literal("gt"), param: z.number(), message: z.string().min(1) }),
  z.object({ rule: z.literal("gte"), param: z.number(), message: z.string().min(1) }),
  z.object({ rule: z.literal("eq"), param: z.any(), message: z.string().min(1) }),
  z.object({ rule: z.literal("ne"), param: z.any(), message: z.string().min(1) }),
  z.object({
    rule: z.literal("regex"),
    param: z.string().min(1),
    message: z.string().min(1)
  })
]);
const validationRulesArraySchema = z.array(validationRuleSchema).max(32);
const isEmpty = (v) => v == null || typeof v === "string" && v.trim() === "" || Array.isArray(v) && v.length === 0;
function evalRule(rule, value, kind) {
  if (rule.rule === "required") {
    return isEmpty(value) ? rule.message : null;
  }
  if (Array.isArray(value)) {
    for (const item of value) {
      const msg = evalScalarRule(rule, item, kind);
      if (msg) return msg;
    }
    return null;
  }
  return evalScalarRule(rule, value, kind);
}
function evalScalarRule(rule, value, kind) {
  switch (rule.rule) {
    case "required":
      return isEmpty(value) ? rule.message : null;
    case "date": {
      if (isEmpty(value)) return null;
      const d = new Date(String(value));
      return isNaN(d.getTime()) ? rule.message : null;
    }
    case "integer": {
      if (isEmpty(value)) return null;
      const n = Number(value);
      return Number.isInteger(n) ? null : rule.message;
    }
    case "float": {
      if (isEmpty(value)) return null;
      const n = Number(value);
      return Number.isFinite(n) ? null : rule.message;
    }
    case "lt":
      if (isEmpty(value)) return null;
      return Number(value) < rule.param ? null : rule.message;
    case "lte":
      if (isEmpty(value)) return null;
      return Number(value) <= rule.param ? null : rule.message;
    case "gt":
      if (isEmpty(value)) return null;
      return Number(value) > rule.param ? null : rule.message;
    case "gte":
      if (isEmpty(value)) return null;
      return Number(value) >= rule.param ? null : rule.message;
    case "eq":
      if (isEmpty(value)) return null;
      return value === rule.param ? null : rule.message;
    case "ne":
      if (isEmpty(value)) return null;
      return value !== rule.param ? null : rule.message;
    case "regex":
      if (isEmpty(value)) return null;
      const regexResult = testLinearRegex(String(rule.param), String(value), {
        maxPatternLength: MAX_CUSTOM_FIELD_REGEX_PATTERN_LENGTH,
        maxInputLength: MAX_CUSTOM_FIELD_REGEX_INPUT_LENGTH
      });
      return regexResult.ok && regexResult.matched ? null : rule.message;
    default:
      return null;
  }
}
function countPresentValueKeys(values) {
  let count = 0;
  for (const key of Object.keys(values)) {
    if (values[key] !== void 0) count++;
  }
  return count;
}
function validateValuesAgainstDefs(values, defs, options = {}) {
  const errors = {};
  if (options.rejectUndeclaredKeys) {
    const allowedKeys = new Set(defs.map((def) => def.key));
    for (const key of Object.keys(values)) {
      if (values[key] === void 0) continue;
      if (!allowedKeys.has(key)) {
        errors[`cf_${key}`] = UNKNOWN_CUSTOM_FIELD_ERROR;
      }
    }
  }
  if (countPresentValueKeys(values) > MAX_CUSTOM_FIELD_KEYS_PER_RECORD) {
    errors._customFields = TOO_MANY_CUSTOM_FIELDS_ERROR;
  }
  for (const def of defs) {
    const cfg = def?.configJson || {};
    const rules = Array.isArray(cfg.validation) ? cfg.validation : [];
    if (rules.length === 0) continue;
    const value = values[def.key];
    for (const r of rules) {
      const msg = evalRule(r, value, def.kind);
      if (msg) {
        errors[`cf_${def.key}`] = msg;
        break;
      }
    }
  }
  return { ok: Object.keys(errors).length === 0, fieldErrors: errors };
}
export {
  MAX_CUSTOM_FIELD_KEYS_PER_RECORD,
  MAX_CUSTOM_FIELD_REGEX_INPUT_LENGTH,
  MAX_CUSTOM_FIELD_REGEX_PATTERN_LENGTH,
  TOO_MANY_CUSTOM_FIELDS_ERROR,
  UNKNOWN_CUSTOM_FIELD_ERROR,
  VALIDATION_RULES,
  validateValuesAgainstDefs,
  validationRuleSchema,
  validationRulesArraySchema
};
//# sourceMappingURL=validation.js.map
