import { ContextResolver } from "./ContextResolver.js";
import { createApiError, getLogger } from "@inkeep/agents-core";
import Ajv from "ajv";

//#region src/domains/run/context/validation.ts
const logger = getLogger("context-validation");
const ajv = new Ajv({
	allErrors: true,
	strict: false
});
const HTTP_REQUEST_PARTS = ["headers"];
const MAX_SCHEMA_CACHE_SIZE = 1e3;
const schemaCache = /* @__PURE__ */ new Map();
function isValidHttpRequest(obj) {
	return obj != null && typeof obj === "object" && !Array.isArray(obj) && "headers" in obj;
}
function getCachedValidator(schema) {
	const key = JSON.stringify(schema);
	if (schemaCache.has(key)) {
		const validator$1 = schemaCache.get(key);
		if (!validator$1) throw new Error("Unexpected: validator not found in cache after has() check");
		schemaCache.delete(key);
		schemaCache.set(key, validator$1);
		return validator$1;
	}
	if (schemaCache.size >= MAX_SCHEMA_CACHE_SIZE) {
		const firstKey = schemaCache.keys().next().value;
		if (firstKey) schemaCache.delete(firstKey);
	}
	const permissiveSchema = makeSchemaPermissive(schema);
	const validator = ajv.compile(permissiveSchema);
	schemaCache.set(key, validator);
	return validator;
}
function makeSchemaPermissive(schema) {
	if (!schema || typeof schema !== "object") return schema;
	const permissiveSchema = { ...schema };
	if (permissiveSchema.type === "object") {
		permissiveSchema.additionalProperties = true;
		if (permissiveSchema.properties && typeof permissiveSchema.properties === "object") {
			const newProperties = {};
			for (const [key, value] of Object.entries(permissiveSchema.properties)) newProperties[key] = makeSchemaPermissive(value);
			permissiveSchema.properties = newProperties;
		}
	}
	if (permissiveSchema.type === "array" && permissiveSchema.items) permissiveSchema.items = makeSchemaPermissive(permissiveSchema.items);
	if (permissiveSchema.oneOf) permissiveSchema.oneOf = permissiveSchema.oneOf.map(makeSchemaPermissive);
	if (permissiveSchema.anyOf) permissiveSchema.anyOf = permissiveSchema.anyOf.map(makeSchemaPermissive);
	if (permissiveSchema.allOf) permissiveSchema.allOf = permissiveSchema.allOf.map(makeSchemaPermissive);
	return permissiveSchema;
}
function validationHelper(jsonSchema) {
	return getCachedValidator(jsonSchema);
}
function validateAgainstJsonSchema(jsonSchema, context) {
	return validationHelper(jsonSchema)(context);
}
function filterByJsonSchema(data, schema) {
	if (!schema || data === null || data === void 0) return data;
	if (schema.type === "object" && schema.properties && typeof data === "object" && !Array.isArray(data)) {
		const filtered = {};
		for (const [key, propSchema] of Object.entries(schema.properties)) if (key in data) filtered[key] = filterByJsonSchema(data[key], propSchema);
		return filtered;
	}
	if (schema.type === "array" && schema.items && Array.isArray(data)) return data.map((item) => filterByJsonSchema(item, schema.items));
	if (schema.anyOf && Array.isArray(schema.anyOf)) {
		for (const subSchema of schema.anyOf) if (subSchema.type && typeof data === subSchema.type) return filterByJsonSchema(data, subSchema);
		return filterByJsonSchema(data, schema.anyOf[0]);
	}
	return data;
}
function filterContextToSchemaKeys(validatedContext, headersSchema) {
	if (!headersSchema || !validatedContext) return validatedContext;
	const filteredHeaders = filterByJsonSchema(validatedContext, headersSchema);
	if (filteredHeaders !== null && filteredHeaders !== void 0) {
		if (typeof filteredHeaders === "object" && Object.keys(filteredHeaders).length > 0) return filteredHeaders;
		if (typeof filteredHeaders !== "object") return filteredHeaders;
	}
	return {};
}
async function validateHttpRequestHeaders(headersSchema, httpRequest) {
	const errors = [];
	let validatedContext = {};
	if (!isValidHttpRequest(httpRequest)) return {
		valid: false,
		errors: [{
			field: "httpRequest",
			message: "Invalid HTTP request format - must contain headers"
		}]
	};
	try {
		if (headersSchema && httpRequest.headers !== void 0) try {
			const validate = validationHelper(headersSchema);
			if (validate(httpRequest.headers)) validatedContext = httpRequest.headers;
			else if (validate.errors) for (const error of validate.errors) errors.push({
				field: `headers.${error.instancePath || "root"}`,
				message: `headers ${error.message}`,
				value: error.data
			});
		} catch (validationError) {
			errors.push({
				field: "headers",
				message: `Failed to validate headers: ${validationError instanceof Error ? validationError.message : "Unknown error"}`
			});
		}
		const filteredContext = errors.length === 0 ? filterContextToSchemaKeys(validatedContext, headersSchema) : void 0;
		return {
			valid: errors.length === 0,
			errors,
			validatedContext: filteredContext
		};
	} catch (error) {
		logger.error({ error: error instanceof Error ? error.message : "Unknown error" }, "Failed to validate headers schema");
		return {
			valid: false,
			errors: [{
				field: "schema",
				message: "Failed to validate headers schema"
			}]
		};
	}
}
async function fetchExistingHeaders({ executionContext, contextConfig, conversationId, credentialStores }) {
	const headers$1 = await new ContextResolver(executionContext, credentialStores).resolveHeaders(conversationId, contextConfig.id);
	if (Object.keys(headers$1).length > 0) return {
		valid: true,
		errors: [],
		validatedContext: headers$1
	};
	throw new Error("No headers found in cache. Please provide headers in request.");
}
async function validateHeaders({ executionContext, conversationId, parsedRequest, credentialStores }) {
	try {
		const { tenantId, projectId, agentId, project } = executionContext;
		logger.info({
			tenantId,
			projectId,
			agentId
		}, "Validating headers");
		const contextConfig = project.agents[agentId].contextConfig;
		logger.info({ contextConfig }, "Context config found");
		if (!contextConfig) {
			logger.info({ agentId }, "No context config found for agent, skipping validation");
			return {
				valid: true,
				errors: [],
				validatedContext: parsedRequest
			};
		}
		if (!contextConfig.headersSchema) {
			logger.debug({ contextConfigId: contextConfig.id }, "No headers schema defined, accepting any context");
			return {
				valid: true,
				errors: [],
				validatedContext: parsedRequest
			};
		}
		try {
			const schema = contextConfig.headersSchema;
			logger.debug({ contextConfigId: contextConfig.id }, "Using headers schema validation");
			const validationResult = await validateHttpRequestHeaders(schema, parsedRequest);
			if (validationResult.valid) return validationResult;
			try {
				return await fetchExistingHeaders({
					executionContext,
					contextConfig,
					conversationId,
					credentialStores
				});
			} catch (_error) {
				validationResult.errors.push({
					field: "headers",
					message: "Failed to fetch headers from cache"
				});
				return validationResult;
			}
		} catch (error) {
			logger.error({
				contextConfigId: contextConfig.id,
				error: error instanceof Error ? error.message : "Unknown error"
			}, "Failed to compile or validate schema");
			return {
				valid: false,
				errors: [{
					field: "schema",
					message: "Invalid schema definition or validation error"
				}]
			};
		}
	} catch (error) {
		logger.error({
			tenantId: executionContext.tenantId,
			agentId: executionContext.agentId,
			error: error instanceof Error ? error.message : "Unknown error"
		}, "Failed to validate headers");
		return {
			valid: false,
			errors: [{
				field: "validation",
				message: "Context validation failed due to internal error"
			}]
		};
	}
}
async function contextValidationMiddleware(c, next) {
	try {
		const executionContext = c.get("executionContext");
		let { tenantId, projectId, agentId } = executionContext;
		if (!tenantId || !projectId || !agentId) {
			tenantId = c.req.param("tenantId");
			projectId = c.req.param("projectId");
			agentId = c.req.param("agentId");
		}
		if (!tenantId || !projectId || !agentId) return next();
		const conversationId = (c.get("requestBody") || {}).conversationId || "";
		const headers$1 = {};
		c.req.raw.headers.forEach((value, key) => {
			headers$1[key.toLowerCase()] = value;
		});
		const credentialStores = c.get("credentialStores");
		const validationResult = await validateHeaders({
			executionContext,
			conversationId,
			parsedRequest: { headers: headers$1 },
			credentialStores
		});
		if (!validationResult.valid) {
			logger.warn({
				tenantId,
				agentId,
				errors: validationResult.errors
			}, "Headers validation failed");
			throw createApiError({
				code: "bad_request",
				message: `Invalid headers: ${validationResult.errors.map((e) => `${e.field}: ${e.message}`).join(", ")}`
			});
		}
		c.set("validatedContext", validationResult.validatedContext);
		logger.debug({
			tenantId,
			agentId,
			contextKeys: Object.keys(validationResult.validatedContext || {})
		}, "Request context validation successful");
		return next();
	} catch (error) {
		logger.error({ error: error instanceof Error ? error.message : "Unknown error" }, "Context validation middleware error");
		throw createApiError({
			code: "internal_server_error",
			message: "Context validation failed"
		});
	}
}

//#endregion
export { HTTP_REQUEST_PARTS, contextValidationMiddleware, getCachedValidator, isValidHttpRequest, validateAgainstJsonSchema, validateHeaders, validateHttpRequestHeaders, validationHelper };