import { getLogger as getLogger$1 } from "../../../logger.js";
import runDbClient_default from "../../../data/db/runDbClient.js";
import { toolSessionManager } from "../agents/ToolSessionManager.js";
import { extractFullFields, extractPreviewFields } from "../utils/schema-validation.js";
import { agentSessionManager } from "./AgentSession.js";
import { getLedgerArtifacts, getTask, listTaskIdsByContextId, upsertLedgerArtifact } from "@inkeep/agents-core";
import jmespath from "jmespath";

//#region src/domains/run/services/ArtifactService.ts
const logger = getLogger$1("ArtifactService");
/**
* Service class responsible for artifact business logic operations
* Handles database persistence, tool result extraction, and artifact management
* Separated from parsing concerns for better architecture
*/
var ArtifactService = class ArtifactService {
	createdArtifacts = /* @__PURE__ */ new Map();
	static selectorCache = /* @__PURE__ */ new Map();
	constructor(context) {
		this.context = context;
	}
	/**
	* Clear static caches to prevent memory leaks between sessions
	*/
	static clearCaches() {
		ArtifactService.selectorCache.clear();
	}
	/**
	* Update artifact components in the context
	*/
	updateArtifactComponents(artifactComponents) {
		this.context.artifactComponents = artifactComponents;
	}
	/**
	* Get all artifacts for a context from database
	*/
	async getContextArtifacts(contextId) {
		const artifacts = /* @__PURE__ */ new Map();
		const { tenantId, projectId } = this.context.executionContext;
		try {
			const taskIds = await listTaskIdsByContextId(runDbClient_default)({ contextId });
			for (const taskId of taskIds) {
				if (!await getTask(runDbClient_default)({ id: taskId })) {
					logger.warn({ taskId }, "Task not found when fetching artifacts");
					continue;
				}
				const taskArtifacts = await getLedgerArtifacts(runDbClient_default)({
					scopes: {
						tenantId,
						projectId
					},
					taskId
				});
				for (const artifact of taskArtifacts) {
					const toolCallId = artifact.metadata?.toolCallId || "";
					if (toolCallId) {
						const key = `${artifact.artifactId}:${toolCallId}`;
						artifacts.set(key, artifact);
					}
					const taskKey = `${artifact.artifactId}:${artifact.taskId}`;
					artifacts.set(taskKey, artifact);
				}
			}
		} catch (error) {
			logger.error({
				error,
				contextId
			}, "Error loading context artifacts");
		}
		return artifacts;
	}
	/**
	* Create artifact from tool result and request data
	*/
	async createArtifact(request, subAgentId) {
		if (!this.context.sessionId) {
			logger.warn({ request }, "No session ID available for artifact creation");
			return null;
		}
		const toolResult = toolSessionManager.getToolResult(this.context.sessionId, request.toolCallId);
		if (!toolResult) {
			logger.warn({
				request,
				sessionId: this.context.sessionId
			}, "Tool result not found for artifact");
			return null;
		}
		try {
			const toolResultData = toolResult && typeof toolResult === "object" && !Array.isArray(toolResult) ? Object.fromEntries(Object.entries(toolResult).filter(([key]) => key !== "_structureHints")) : toolResult;
			const sanitizedBaseSelector = this.sanitizeJMESPathSelector(request.baseSelector);
			let selectedData = jmespath.search(toolResultData, sanitizedBaseSelector);
			if (Array.isArray(selectedData)) selectedData = selectedData.length > 0 ? selectedData[0] : {};
			if (!selectedData) {
				logger.warn({
					request,
					baseSelector: request.baseSelector
				}, "Base selector returned no data - using empty object as fallback");
				selectedData = {};
			}
			const component = this.context.artifactComponents?.find((ac) => ac.name === request.type);
			let summaryData = {};
			let fullData = {};
			let previewSchema = null;
			let fullSchema = null;
			if (component?.props) {
				previewSchema = extractPreviewFields(component.props);
				fullSchema = extractFullFields(component.props);
				summaryData = this.extractPropsFromSchema(selectedData, previewSchema, request.detailsSelector || {});
				fullData = this.extractPropsFromSchema(selectedData, fullSchema, request.detailsSelector || {});
			} else {
				summaryData = selectedData;
				fullData = selectedData;
			}
			if (!fullData || Object.keys(fullData).length === 0 || Object.values(fullData).every((val) => val === null || val === void 0 || val === "" || Array.isArray(val) && val.length === 0 || typeof val === "object" && Object.keys(val).length === 0)) fullData = { baseSelector: selectedData };
			const cleanedSummaryData = this.cleanEscapedContent(summaryData);
			const cleanedFullData = this.cleanEscapedContent(fullData);
			const schemaValidation = this.validateExtractedData(request.artifactId, request.type, cleanedSummaryData, cleanedFullData, previewSchema, fullSchema, component?.props);
			const artifactData = {
				artifactId: request.artifactId,
				toolCallId: request.toolCallId,
				name: "Processing...",
				description: "Name and description being generated...",
				type: request.type,
				data: cleanedSummaryData
			};
			await this.persistArtifact(request, cleanedSummaryData, cleanedFullData, subAgentId, schemaValidation);
			await this.cacheArtifact(request.artifactId, request.toolCallId, artifactData, cleanedFullData);
			return artifactData;
		} catch (error) {
			logger.error({
				error,
				request
			}, "Failed to create artifact");
			const errorMessage = error instanceof Error ? error.message : "Unknown error";
			throw new Error(`Artifact creation failed for ${request.artifactId}: ${errorMessage}`);
		}
	}
	/**
	* Get artifact summary data by ID and tool call ID
	*/
	async getArtifactSummary(artifactId, toolCallId, artifactMap) {
		const key = `${artifactId}:${toolCallId}`;
		const { tenantId, projectId } = this.context.executionContext;
		if (this.context.streamRequestId) {
			const cachedArtifact = await agentSessionManager.getArtifactCache(this.context.streamRequestId, key);
			if (cachedArtifact) return this.formatArtifactSummaryData(cachedArtifact, artifactId, toolCallId);
		}
		if (this.createdArtifacts.has(key)) {
			const cached = this.createdArtifacts.get(key);
			if (!cached) {
				logger.warn({
					artifactId,
					toolCallId
				}, "Cached artifact not found");
				return null;
			}
			return this.formatArtifactSummaryData(cached, artifactId, toolCallId);
		}
		if (artifactMap?.has(key)) {
			const artifact = artifactMap.get(key);
			return this.formatArtifactSummaryData(artifact, artifactId, toolCallId);
		}
		try {
			if (!projectId || !this.context.taskId) {
				logger.warn({
					artifactId,
					toolCallId
				}, "No projectId or taskId available for artifact lookup");
				return null;
			}
			let artifacts = [];
			artifacts = await getLedgerArtifacts(runDbClient_default)({
				scopes: {
					tenantId,
					projectId
				},
				artifactId,
				toolCallId
			});
			if (artifacts.length > 0) return this.formatArtifactSummaryData(artifacts[0], artifactId, toolCallId);
			artifacts = await getLedgerArtifacts(runDbClient_default)({
				scopes: {
					tenantId,
					projectId
				},
				artifactId,
				taskId: this.context.taskId
			});
			if (artifacts.length > 0) return this.formatArtifactSummaryData(artifacts[0], artifactId, toolCallId);
		} catch (error) {
			logger.warn({
				artifactId,
				toolCallId,
				taskId: this.context.taskId,
				error
			}, "Failed to fetch artifact");
		}
		return null;
	}
	/**
	* Get artifact full data by ID and tool call ID
	*/
	async getArtifactFull(artifactId, toolCallId, artifactMap) {
		const key = `${artifactId}:${toolCallId}`;
		const { tenantId, projectId } = this.context.executionContext;
		if (this.context.streamRequestId) {
			const cachedArtifact = await agentSessionManager.getArtifactCache(this.context.streamRequestId, key);
			if (cachedArtifact) return this.formatArtifactFullData(cachedArtifact, artifactId, toolCallId);
		}
		if (this.createdArtifacts.has(key)) {
			const cached = this.createdArtifacts.get(key);
			if (!cached) {
				logger.warn({
					artifactId,
					toolCallId
				}, "Cached artifact not found");
				return null;
			}
			return this.formatArtifactFullData(cached, artifactId, toolCallId);
		}
		if (artifactMap?.has(key)) {
			const artifact = artifactMap.get(key);
			return this.formatArtifactFullData(artifact, artifactId, toolCallId);
		}
		try {
			if (!projectId || !this.context.taskId) {
				logger.warn({
					artifactId,
					toolCallId
				}, "No projectId or taskId available for artifact lookup");
				return null;
			}
			let artifacts = [];
			artifacts = await getLedgerArtifacts(runDbClient_default)({
				scopes: {
					tenantId,
					projectId
				},
				artifactId,
				toolCallId
			});
			if (artifacts.length > 0) return this.formatArtifactFullData(artifacts[0], artifactId, toolCallId);
			artifacts = await getLedgerArtifacts(runDbClient_default)({
				scopes: {
					tenantId,
					projectId
				},
				artifactId,
				taskId: this.context.taskId
			});
			if (artifacts.length > 0) return this.formatArtifactFullData(artifacts[0], artifactId, toolCallId);
		} catch (error) {
			logger.warn({
				artifactId,
				toolCallId,
				taskId: this.context.taskId,
				error
			}, "Failed to fetch artifact");
		}
		return null;
	}
	/**
	* Format raw artifact to standardized summary data format
	*/
	formatArtifactSummaryData(artifact, artifactId, toolCallId) {
		let data = artifact.parts?.[0]?.data?.summary;
		let dataSource = "parts[0].data.summary";
		if (!data || typeof data === "object" && Object.keys(data).length === 0) {
			data = artifact.parts?.[0]?.data;
			if (data && !(typeof data === "object" && Object.keys(data).length === 0)) {
				dataSource = "parts[0].data (fallback)";
				logger.debug({
					artifactId,
					toolCallId,
					dataSource
				}, "Using fallback data source for artifact summary");
			} else {
				data = artifact.data;
				if (data && !(typeof data === "object" && Object.keys(data).length === 0)) {
					dataSource = "artifact.data (fallback)";
					logger.debug({
						artifactId,
						toolCallId,
						dataSource
					}, "Using fallback data source for artifact summary");
				} else {
					data = {};
					dataSource = "empty (no data found)";
					logger.warn({
						artifactId,
						toolCallId,
						artifactStructure: {
							hasParts: !!artifact.parts,
							partsLength: artifact.parts?.length,
							hasPartsData: !!artifact.parts?.[0]?.data,
							hasPartsSummary: !!artifact.parts?.[0]?.data?.summary,
							hasArtifactData: !!artifact.data,
							artifactKeys: Object.keys(artifact || {})
						}
					}, "No valid data found for artifact summary - using empty object");
				}
			}
		}
		return {
			artifactId,
			toolCallId,
			name: artifact.name || "Processing...",
			description: artifact.description || "Name and description being generated...",
			type: artifact.metadata?.artifactType || artifact.artifactType,
			data
		};
	}
	/**
	* Format raw artifact to standardized full data format
	*/
	formatArtifactFullData(artifact, artifactId, toolCallId) {
		let data = artifact.parts?.[0]?.data?.full;
		let dataSource = "parts[0].data.full";
		if (!data || typeof data === "object" && Object.keys(data).length === 0) {
			data = artifact.parts?.[0]?.data;
			if (data && !(typeof data === "object" && Object.keys(data).length === 0)) {
				dataSource = "parts[0].data (fallback)";
				logger.debug({
					artifactId,
					toolCallId,
					dataSource
				}, "Using fallback data source for artifact full data");
			} else {
				data = artifact.data;
				if (data && !(typeof data === "object" && Object.keys(data).length === 0)) {
					dataSource = "artifact.data (fallback)";
					logger.debug({
						artifactId,
						toolCallId,
						dataSource
					}, "Using fallback data source for artifact full data");
				} else {
					data = {};
					dataSource = "empty (no data found)";
					logger.warn({
						artifactId,
						toolCallId,
						artifactStructure: {
							hasParts: !!artifact.parts,
							partsLength: artifact.parts?.length,
							hasPartsData: !!artifact.parts?.[0]?.data,
							hasPartsFull: !!artifact.parts?.[0]?.data?.full,
							hasArtifactData: !!artifact.data,
							artifactKeys: Object.keys(artifact || {})
						}
					}, "No valid data found for artifact full data - using empty object");
				}
			}
		}
		return {
			artifactId,
			toolCallId,
			name: artifact.name || "Processing...",
			description: artifact.description || "Name and description being generated...",
			type: artifact.metadata?.artifactType || artifact.artifactType,
			data
		};
	}
	/**
	* Validate extracted data against the schemas used for extraction
	*/
	validateExtractedData(artifactId, artifactType, summaryData, fullData, previewSchema, fullSchema, originalProps) {
		const validateAgainstSchema = (data, schema) => {
			const actualFields = Object.keys(data || {});
			const expectedFields = schema?.properties ? Object.keys(schema.properties) : [];
			const missingFields = expectedFields.filter((field) => !(field in (data || {})));
			const extraFields = actualFields.filter((field) => !expectedFields.includes(field));
			const missingRequired = (schema?.required || []).filter((field) => !(field in (data || {})));
			return {
				hasExpectedFields: missingFields.length === 0,
				missingFields,
				extraFields,
				expectedFields,
				actualFields,
				hasRequiredFields: missingRequired.length === 0,
				missingRequired
			};
		};
		const summaryValidation = validateAgainstSchema(summaryData, previewSchema);
		const fullValidation = validateAgainstSchema(fullData, fullSchema);
		if (!summaryValidation.hasRequiredFields) {
			`${summaryValidation.missingRequired.join(", ")}${artifactType}${summaryValidation.missingRequired.join(", ")}${summaryValidation.actualFields.join(", ")}`;
			logger.error({
				artifactId,
				artifactType,
				requiredFields: summaryValidation.missingRequired,
				actualFields: summaryValidation.actualFields,
				schemaExpected: previewSchema?.properties ? Object.keys(previewSchema.properties) : []
			}, "Artifact creation failed due to missing required fields - continuing with generation");
			return {
				summary: summaryValidation,
				full: fullValidation,
				schemaFound: !!previewSchema
			};
		}
		if (!summaryValidation.hasExpectedFields || summaryValidation.extraFields.length > 0) logger.warn({
			artifactId,
			artifactType,
			dataType: "summary",
			expectedFields: summaryValidation.expectedFields,
			actualFields: summaryValidation.actualFields,
			missingFields: summaryValidation.missingFields,
			extraFields: summaryValidation.extraFields
		}, "Summary data structure does not match preview schema");
		if (!fullValidation.hasExpectedFields || fullValidation.extraFields.length > 0) logger.warn({
			artifactId,
			artifactType,
			dataType: "full",
			expectedFields: fullValidation.expectedFields,
			actualFields: fullValidation.actualFields,
			missingFields: fullValidation.missingFields,
			extraFields: fullValidation.extraFields
		}, "Full data structure does not match full schema");
		return {
			summary: summaryValidation,
			full: fullValidation,
			schemaFound: !!originalProps
		};
	}
	/**
	* Persist artifact to database vian agent session
	*/
	async persistArtifact(request, summaryData, fullData, subAgentId, schemaValidation) {
		const effectiveAgentId = subAgentId || this.context.subAgentId;
		if (this.context.streamRequestId && effectiveAgentId && this.context.taskId) await agentSessionManager.recordEvent(this.context.streamRequestId, "artifact_saved", effectiveAgentId, {
			artifactId: request.artifactId,
			taskId: this.context.taskId,
			toolCallId: request.toolCallId,
			artifactType: request.type,
			summaryData,
			data: fullData,
			subAgentId: effectiveAgentId,
			metadata: {
				toolCallId: request.toolCallId,
				baseSelector: request.baseSelector,
				detailsSelector: request.detailsSelector,
				sessionId: this.context.sessionId,
				artifactType: request.type
			},
			schemaValidation: schemaValidation || {
				summary: {
					hasExpectedFields: true,
					missingFields: [],
					extraFields: [],
					expectedFields: [],
					actualFields: [],
					hasRequiredFields: true,
					missingRequired: []
				},
				full: {
					hasExpectedFields: true,
					missingFields: [],
					extraFields: [],
					expectedFields: [],
					actualFields: [],
					hasRequiredFields: true,
					missingRequired: []
				},
				schemaFound: false
			},
			tenantId: this.context.executionContext.tenantId,
			projectId: this.context.executionContext.projectId,
			contextId: this.context.contextId,
			pendingGeneration: true
		});
		else logger.warn({
			artifactId: request.artifactId,
			hasStreamRequestId: !!this.context.streamRequestId,
			hasAgentId: !!effectiveAgentId,
			hasTaskId: !!this.context.taskId,
			passedAgentId: subAgentId,
			contextAgentId: this.context.subAgentId
		}, "Skipping artifact_saved event - missing required context");
	}
	/**
	* Cache artifact for immediate access
	*/
	async cacheArtifact(artifactId, toolCallId, artifactData, fullData) {
		const cacheKey = `${artifactId}:${toolCallId}`;
		const artifactForCache = {
			...artifactData,
			parts: [{ data: {
				summary: artifactData.data,
				data: fullData
			} }],
			metadata: {
				artifactType: artifactData.type,
				toolCallId
			},
			taskId: this.context.taskId
		};
		this.createdArtifacts.set(cacheKey, artifactForCache);
		if (this.context.streamRequestId) await agentSessionManager.setArtifactCache(this.context.streamRequestId, cacheKey, artifactForCache);
	}
	/**
	* Sanitize JMESPath selector to fix common syntax issues (with caching)
	*/
	sanitizeJMESPathSelector(selector) {
		const cached = ArtifactService.selectorCache.get(selector);
		if (cached !== void 0) return cached;
		let sanitized = selector.replace(/=="([^"]*)"/g, "=='$1'");
		sanitized = sanitized.replace(/\[\?(\w+)\s*~\s*contains\(@,\s*"([^"]*)"\)\]/g, "[?contains($1, `$2`)]");
		sanitized = sanitized.replace(/\[\?(\w+)\s*~\s*contains\(@,\s*'([^']*)'\)\]/g, "[?contains($1, `$2`)]");
		sanitized = sanitized.replace(/\s*~\s*/g, " ");
		if (ArtifactService.selectorCache.size < 1e3) ArtifactService.selectorCache.set(selector, sanitized);
		return sanitized;
	}
	/**
	* Save an already-created artifact directly to the database
	* Used by AgentSession to save artifacts after name/description generation
	*/
	async saveArtifact(artifact) {
		let summaryData = artifact.summaryData || artifact.data;
		let fullData = artifact.data;
		const { tenantId, projectId } = this.context.executionContext;
		if (this.context.artifactComponents) {
			const artifactComponent = this.context.artifactComponents.find((ac) => ac.name === artifact.type);
			if (artifactComponent?.props) try {
				const schema = artifactComponent.props;
				const previewSchema = extractPreviewFields(schema);
				const fullSchema = extractFullFields(schema);
				summaryData = this.filterBySchema(artifact.data, previewSchema);
				fullData = this.filterBySchema(artifact.data, fullSchema);
			} catch (error) {
				logger.warn({
					artifactType: artifact.type,
					error: error instanceof Error ? error.message : "Unknown error"
				}, "Failed to extract preview/full fields from schema, using full data for both");
			}
		}
		const artifactToSave = {
			artifactId: artifact.artifactId,
			name: artifact.name,
			description: artifact.description,
			type: artifact.type,
			taskId: this.context.taskId,
			parts: [{
				kind: "data",
				data: {
					summary: summaryData,
					full: fullData
				}
			}],
			metadata: artifact.metadata || {},
			createdAt: (/* @__PURE__ */ new Date()).toISOString()
		};
		const result = await upsertLedgerArtifact(runDbClient_default)({
			scopes: {
				tenantId,
				projectId
			},
			contextId: this.context.contextId,
			taskId: this.context.taskId,
			toolCallId: artifact.toolCallId,
			artifact: artifactToSave
		});
		if (!result.created && result.existing) logger.debug({
			artifactId: artifact.artifactId,
			taskId: this.context.taskId
		}, "Artifact already exists, skipping duplicate creation");
	}
	/**
	* Clean up over-escaped strings that have been through multiple JSON serialization cycles
	*/
	cleanEscapedContent(value) {
		if (typeof value === "string") {
			let cleaned = value;
			cleaned = cleaned.replace(/\u0000/g, "").replace(/[\u0001-\u0008\u000B\u000C\u000E-\u001F]/g, "");
			cleaned = cleaned.replace(/\\"([^"]+)\\"/g, "\"$1\"").replace(/\\'/g, "'").replace(/\\`/g, "`").replace(/\\\\/g, "\\");
			const maxIterations = 10;
			let iteration = 0;
			let previousLength;
			do {
				previousLength = cleaned.length;
				cleaned = cleaned.replace(/\\\\\\\\n/g, "\n").replace(/\\\\\\\\/g, "\\").replace(/\\\\n/g, "\n").replace(/\\\\/g, "\\").replace(/\\n/g, "\n").replace(/\\"/g, "\"").replace(/\\'/g, "'");
				iteration++;
			} while (cleaned.length !== previousLength && iteration < maxIterations);
			cleaned = cleaned.replace(/\\\\/g, "\\");
			return cleaned;
		}
		if (Array.isArray(value)) return value.map((item) => this.cleanEscapedContent(item));
		if (value && typeof value === "object") {
			const cleaned = {};
			for (const [key, val] of Object.entries(value)) cleaned[key] = this.cleanEscapedContent(val);
			return cleaned;
		}
		return value;
	}
	/**
	* Extract properties from data using schema-defined fields and custom selectors
	*/
	extractPropsFromSchema(item, schema, customSelectors) {
		const extracted = {};
		if (schema.properties) for (const fieldName of Object.keys(schema.properties)) try {
			const customSelector = customSelectors[fieldName];
			let rawValue;
			if (customSelector) {
				const sanitizedSelector = this.sanitizeJMESPathSelector(customSelector);
				rawValue = jmespath.search(item, sanitizedSelector);
			} else rawValue = item[fieldName];
			if (rawValue !== null && rawValue !== void 0) extracted[fieldName] = this.cleanEscapedContent(rawValue);
		} catch (error) {
			logger.warn({
				fieldName,
				error: error instanceof Error ? error.message : "Unknown error"
			}, "Failed to extract schema field");
			const fallbackValue = item[fieldName];
			if (fallbackValue !== null && fallbackValue !== void 0) extracted[fieldName] = this.cleanEscapedContent(fallbackValue);
		}
		return extracted;
	}
	/**
	* Filter extracted props based on schema
	*/
	filterBySchema(props, schema) {
		if (!schema?.properties) return props;
		const filtered = {};
		for (const key of Object.keys(schema.properties)) if (key in props) filtered[key] = props[key];
		return filtered;
	}
};

//#endregion
export { ArtifactService };