import { a as normalizeLowercaseStringOrEmpty } from "./string-coerce-mnp54Vah.js";
import { t as PluginLruCache } from "./plugin-cache-primitives-BaxqicKH.js";
import { t as sanitizeTerminalText } from "./safe-text-BkQYdJi1.js";
import { n as findJsonSchemaShapeError, r as normalizeJsonSchemaForTypeBox, t as applyJsonSchemaDefaults } from "./json-schema-defaults-DVQXyarF.js";
import { Compile } from "typebox/compile";
import { Format } from "typebox/format";
//#region src/config/allowed-values.ts
const MAX_ALLOWED_VALUES_HINT = 12;
const MAX_ALLOWED_VALUE_CHARS = 160;
function truncateHintText(text, limit) {
	if (text.length <= limit) return text;
	return `${text.slice(0, limit)}... (+${text.length - limit} chars)`;
}
function safeStringify(value) {
	try {
		const serialized = JSON.stringify(value);
		if (serialized !== void 0) return serialized;
	} catch {}
	return String(value);
}
function toAllowedValueLabel(value) {
	if (typeof value === "string") return JSON.stringify(truncateHintText(value, MAX_ALLOWED_VALUE_CHARS));
	return truncateHintText(safeStringify(value), MAX_ALLOWED_VALUE_CHARS);
}
function toAllowedValueValue(value) {
	if (typeof value === "string") return value;
	return safeStringify(value);
}
function toAllowedValueDedupKey(value) {
	if (value === null) return "null:null";
	const kind = typeof value;
	if (kind === "string") return `string:${value}`;
	return `${kind}:${safeStringify(value)}`;
}
/** Summarizes enum/allowed-value candidates for compact validation error hints. */
function summarizeAllowedValues(values) {
	if (values.length === 0) return null;
	const deduped = [];
	const seenValues = /* @__PURE__ */ new Set();
	for (const item of values) {
		const dedupeKey = toAllowedValueDedupKey(item);
		if (seenValues.has(dedupeKey)) continue;
		seenValues.add(dedupeKey);
		deduped.push({
			value: toAllowedValueValue(item),
			label: toAllowedValueLabel(item)
		});
	}
	const shown = deduped.slice(0, MAX_ALLOWED_VALUES_HINT);
	const hiddenCount = deduped.length - shown.length;
	const formattedCore = shown.map((entry) => entry.label).join(", ");
	const formatted = hiddenCount > 0 ? `${formattedCore}, ... (+${hiddenCount} more)` : formattedCore;
	return {
		values: shown.map((entry) => entry.value),
		hiddenCount,
		formatted
	};
}
function messageAlreadyIncludesAllowedValues(message) {
	const lower = normalizeLowercaseStringOrEmpty(message);
	return lower.includes("(allowed:") || lower.includes("expected one of");
}
/** Appends an allowed-values hint unless the validation message already includes one. */
function appendAllowedValuesHint(message, summary) {
	if (messageAlreadyIncludesAllowedValues(message)) return message;
	return `${message} (allowed: ${summary.formatted})`;
}
//#endregion
//#region src/plugins/schema-validator.ts
const schemaCache = new PluginLruCache(512);
const annotationOnlyFormats = [
	"date-time",
	"date",
	"duration",
	"email",
	"hostname",
	"idn-email",
	"idn-hostname",
	"ipv4",
	"ipv6",
	"iri-reference",
	"iri",
	"json-pointer-uri-fragment",
	"json-pointer",
	"regex",
	"relative-json-pointer",
	"time",
	"uri-reference",
	"uri-template",
	"url",
	"uuid"
];
function fingerprintSchema(schema) {
	return JSON.stringify(schema);
}
function schemaHasDefaults(schema) {
	if (!schema || typeof schema !== "object") return false;
	if (Array.isArray(schema)) return schema.some((item) => schemaHasDefaults(item));
	const record = schema;
	if (Object.hasOwn(record, "default")) return true;
	return Object.values(record).some((value) => schemaHasDefaults(value));
}
function cloneValidationValue(value) {
	if (value === void 0 || value === null) return value;
	return structuredClone(value);
}
function compileSchema(schema) {
	return Compile(normalizeJsonSchemaForTypeBox(schema));
}
function relaxConditionalRequiredKeywords(schema, insideConditionalBranch = false) {
	if (Array.isArray(schema)) return schema.map((entry) => relaxConditionalRequiredKeywords(entry, insideConditionalBranch));
	if (!schema || typeof schema !== "object") return schema;
	return Object.fromEntries(Object.entries(schema).filter(([key]) => !(insideConditionalBranch && key === "required")).map(([key, value]) => [key, typeof value === "boolean" || value && typeof value === "object" ? relaxConditionalRequiredKeywords(value, insideConditionalBranch || key === "then" || key === "else") : value]));
}
function withPluginFormatSemantics(callback) {
	const previousFormats = Format.Entries();
	Format.Set("uri", (value) => URL.canParse(value));
	for (const format of annotationOnlyFormats) Format.Set(format, () => true);
	try {
		return callback();
	} finally {
		Format.Clear();
		for (const [format, check] of previousFormats) Format.Set(format, check);
	}
}
function checkSchema(validate, value) {
	return withPluginFormatSemantics(() => {
		if (validate.Check(value)) return null;
		return [...validate.Errors(value)];
	});
}
function applyDefaultsWithPluginFormatSemantics(schema, value) {
	return withPluginFormatSemantics(() => applyJsonSchemaDefaults(schema, value));
}
function isDefaultActivatedConditionalFailure(params) {
	if (checkSchema(compileSchema(relaxConditionalRequiredKeywords(params.schema)), params.defaultedValue)) return false;
	return checkSchema(compileSchema(params.schema), params.originalValue) === null;
}
function normalizeErrorPath(instancePath) {
	const path = instancePath?.replace(/^\//, "").replace(/\//g, ".");
	return path && path.length > 0 ? path : "<root>";
}
function appendPathSegment(path, segment) {
	const trimmed = segment.trim();
	if (!trimmed) return path;
	if (path === "<root>") return trimmed;
	return `${path}.${trimmed}`;
}
function firstStringParam(value) {
	if (typeof value === "string" && value.trim()) return value;
	if (Array.isArray(value)) return value.find((entry) => typeof entry === "string" && entry.trim().length > 0) ?? null;
	return null;
}
function resolveMissingProperty(error) {
	if (error.keyword !== "required" && error.keyword !== "dependentRequired" && error.keyword !== "dependencies") return null;
	return firstStringParam(error.params?.missingProperty) ?? firstStringParam(error.params?.requiredProperties) ?? firstStringParam(error.params?.dependencies);
}
function resolveValidationErrorPath(error) {
	const basePath = normalizeErrorPath(error.instancePath);
	const missingProperty = resolveMissingProperty(error);
	if (!missingProperty) return basePath;
	return appendPathSegment(basePath, missingProperty);
}
function extractAllowedValues(error) {
	if (error.keyword === "enum") {
		const allowedValues = error.params?.allowedValues;
		return Array.isArray(allowedValues) ? allowedValues : null;
	}
	if (error.keyword === "const") {
		const params = error.params;
		if (!params || !Object.hasOwn(params, "allowedValue")) return null;
		return [params.allowedValue];
	}
	return null;
}
function getAllowedValuesSummary(error) {
	const allowedValues = extractAllowedValues(error);
	if (!allowedValues) return null;
	return summarizeAllowedValues(allowedValues);
}
function resolveAdditionalProperty(error) {
	if (error.keyword !== "additionalProperties") return;
	return firstStringParam(error.params?.additionalProperty) ?? void 0;
}
function resolveAdditionalProperties(error) {
	if (error.keyword !== "additionalProperties") return [];
	const additionalProperties = error.params?.additionalProperties;
	if (Array.isArray(additionalProperties)) return additionalProperties.filter((entry) => typeof entry === "string");
	const additionalProperty = error.params?.additionalProperty;
	return typeof additionalProperty === "string" ? [additionalProperty] : [];
}
function formatRequiredMessage(error) {
	const missingProperty = resolveMissingProperty(error);
	if (!missingProperty) return null;
	return `must have required property '${missingProperty}'`;
}
function formatAdditionalPropertiesMessage(error) {
	const additionalProperties = resolveAdditionalProperties(error);
	if (additionalProperties.length === 0) return null;
	return `must not have additional properties: ${additionalProperties.map((entry) => `"${entry}"`).join(", ")}`;
}
function formatValidationErrorMessage(error) {
	return formatRequiredMessage(error) ?? formatAdditionalPropertiesMessage(error) ?? error.message ?? "invalid";
}
function formatValidationErrors(errors) {
	if (!errors || errors.length === 0) return [{
		path: "<root>",
		message: "invalid config",
		text: "<root>: invalid config"
	}];
	return errors.map((error) => {
		const path = resolveValidationErrorPath(error);
		const baseMessage = formatValidationErrorMessage(error);
		const allowedValuesSummary = getAllowedValuesSummary(error);
		const additionalProperty = resolveAdditionalProperty(error);
		const message = allowedValuesSummary ? appendAllowedValuesHint(baseMessage, allowedValuesSummary) : baseMessage;
		return {
			path,
			message,
			text: `${sanitizeTerminalText(path)}: ${sanitizeTerminalText(message)}`,
			...additionalProperty ? { additionalProperty } : {},
			...allowedValuesSummary ? {
				allowedValues: allowedValuesSummary.values,
				allowedValuesHiddenCount: allowedValuesSummary.hiddenCount
			} : {}
		};
	});
}
/**
* Validate a plugin-owned value against a JSON Schema, optionally hydrating schema defaults.
* The cache key is caller-owned so repeated plugin/schema validations can reuse compiled TypeBox validators.
*/
function validateJsonSchemaValue(params) {
	const schemaError = findJsonSchemaShapeError(params.schema);
	if (schemaError) throw new Error(sanitizeTerminalText(`invalid schema: ${schemaError}`));
	if (!(params.cache !== false)) {
		const validate = compileSchema(params.schema);
		const value = params.applyDefaults && schemaHasDefaults(params.schema) ? applyDefaultsWithPluginFormatSemantics(params.schema, cloneValidationValue(params.value)) : params.value;
		const errors = checkSchema(validate, value);
		if (!errors) return {
			ok: true,
			value
		};
		if (params.applyDefaults && value !== params.value && isDefaultActivatedConditionalFailure({
			schema: params.schema,
			originalValue: params.value,
			defaultedValue: value
		})) return {
			ok: true,
			value
		};
		return {
			ok: false,
			errors: formatValidationErrors(errors)
		};
	}
	const cacheKey = params.applyDefaults ? `${params.cacheKey}::defaults` : params.cacheKey;
	let cached = schemaCache.get(cacheKey);
	const schemaFingerprint = !cached || cached.schema !== params.schema ? fingerprintSchema(params.schema) : void 0;
	if (!cached || cached.schema !== params.schema && cached.schemaFingerprint !== schemaFingerprint) {
		const validate = compileSchema(params.schema);
		cached = {
			hasDefaults: params.applyDefaults ? schemaHasDefaults(params.schema) : false,
			validate,
			schema: params.schema,
			schemaFingerprint: schemaFingerprint ?? fingerprintSchema(params.schema)
		};
		schemaCache.set(cacheKey, cached);
	} else if (cached.schema !== params.schema) cached.schema = params.schema;
	const value = params.applyDefaults && cached.hasDefaults ? applyDefaultsWithPluginFormatSemantics(params.schema, cloneValidationValue(params.value)) : params.value;
	const errors = checkSchema(cached.validate, value);
	if (!errors) return {
		ok: true,
		value
	};
	if (params.applyDefaults && value !== params.value && isDefaultActivatedConditionalFailure({
		schema: params.schema,
		originalValue: params.value,
		defaultedValue: value
	})) return {
		ok: true,
		value
	};
	return {
		ok: false,
		errors: formatValidationErrors(errors)
	};
}
//#endregion
export { appendAllowedValuesHint as n, summarizeAllowedValues as r, validateJsonSchemaValue as t };
