import { env } from "../../../env.js";
import runDbClient_default from "../../../data/db/runDbClient.js";
import { getLogger as getLogger$1 } from "../../../logger.js";
import { dispatchExecution } from "../../run/services/TriggerService.js";
import { requireProjectPermission } from "../../../middleware/projectAccess.js";
import { speakeasyOffsetLimitPagination } from "../../../utils/speakeasy.js";
import { assertCanMutateTrigger, validateRunAsUserId } from "./triggerHelpers.js";
import { OpenAPIHono, z } from "@hono/zod-openapi";
import { DateTimeFilterQueryParamsSchema, PaginationQueryParamsSchema, PartSchema, TenantProjectAgentIdParamsSchema, TenantProjectAgentParamsSchema, TriggerApiInsertSchema, TriggerApiUpdateSchema, TriggerInvocationListResponse, TriggerInvocationResponse, TriggerInvocationStatusEnum, TriggerWithWebhookUrlListResponse, TriggerWithWebhookUrlResponse, TriggerWithWebhookUrlWithWarningResponse, commonGetErrorResponses, createApiError, createTrigger, deleteTrigger, errorSchemaFactory, generateId, getCredentialReference, getTriggerById, getTriggerInvocationById, hashAuthenticationHeaders, listTriggerInvocationsPaginated, listTriggersPaginated, updateTrigger } from "@inkeep/agents-core";
import { createProtectedRoute } from "@inkeep/agents-core/middleware";

//#region src/domains/manage/routes/triggers.ts
const logger = getLogger$1("triggers");
const app = new OpenAPIHono();
/**
* Generate webhook URL for a trigger
*/
function generateWebhookUrl(params) {
	const { baseUrl, tenantId, projectId, agentId, triggerId } = params;
	return `${baseUrl}/run/tenants/${tenantId}/projects/${projectId}/agents/${agentId}/triggers/${triggerId}`;
}
/**
* List Triggers for an Agent
*/
app.openapi(createProtectedRoute({
	method: "get",
	path: "/",
	summary: "List Triggers",
	operationId: "list-triggers",
	tags: ["Triggers"],
	permission: requireProjectPermission("view"),
	request: {
		params: TenantProjectAgentParamsSchema,
		query: PaginationQueryParamsSchema
	},
	responses: {
		200: {
			description: "List of triggers retrieved successfully",
			content: { "application/json": { schema: TriggerWithWebhookUrlListResponse } }
		},
		...commonGetErrorResponses
	},
	...speakeasyOffsetLimitPagination
}), async (c) => {
	const db = c.get("db");
	const { tenantId, projectId, agentId } = c.req.valid("param");
	const { page, limit } = c.req.valid("query");
	const apiBaseUrl = env.INKEEP_AGENTS_API_URL;
	const result = await listTriggersPaginated(db)({
		scopes: {
			tenantId,
			projectId,
			agentId
		},
		pagination: {
			page,
			limit
		}
	});
	const dataWithWebhookUrl = result.data.map((trigger) => {
		const { tenantId: _tid, projectId: _pid, agentId: _aid, ...triggerWithoutScopes } = trigger;
		return {
			...triggerWithoutScopes,
			webhookUrl: generateWebhookUrl({
				baseUrl: apiBaseUrl,
				tenantId,
				projectId,
				agentId,
				triggerId: trigger.id
			})
		};
	});
	return c.json({
		data: dataWithWebhookUrl,
		pagination: result.pagination
	});
});
/**
* Get Trigger by ID
*/
app.openapi(createProtectedRoute({
	method: "get",
	path: "/{id}",
	summary: "Get Trigger",
	operationId: "get-trigger-by-id",
	tags: ["Triggers"],
	permission: requireProjectPermission("view"),
	request: { params: TenantProjectAgentIdParamsSchema },
	responses: {
		200: {
			description: "Trigger found",
			content: { "application/json": { schema: TriggerWithWebhookUrlResponse } }
		},
		...commonGetErrorResponses
	}
}), async (c) => {
	const db = c.get("db");
	const { tenantId, projectId, agentId, id } = c.req.valid("param");
	const apiBaseUrl = env.INKEEP_AGENTS_API_URL;
	const trigger = await getTriggerById(db)({
		scopes: {
			tenantId,
			projectId,
			agentId
		},
		triggerId: id
	});
	if (!trigger) throw createApiError({
		code: "not_found",
		message: "Trigger not found"
	});
	const { tenantId: _tid, projectId: _pid, agentId: _aid, ...triggerWithoutScopes } = trigger;
	return c.json({ data: {
		...triggerWithoutScopes,
		webhookUrl: generateWebhookUrl({
			baseUrl: apiBaseUrl,
			tenantId,
			projectId,
			agentId,
			triggerId: trigger.id
		})
	} });
});
/**
* Create Trigger
*/
app.openapi(createProtectedRoute({
	method: "post",
	path: "/",
	summary: "Create Trigger",
	operationId: "create-trigger",
	tags: ["Triggers"],
	permission: requireProjectPermission("edit"),
	request: {
		params: TenantProjectAgentParamsSchema,
		body: { content: { "application/json": { schema: TriggerApiInsertSchema } } }
	},
	responses: {
		201: {
			description: "Trigger created successfully",
			content: { "application/json": { schema: TriggerWithWebhookUrlWithWarningResponse } }
		},
		...commonGetErrorResponses
	}
}), async (c) => {
	const db = c.get("db");
	const { tenantId, projectId, agentId } = c.req.valid("param");
	const body = c.req.valid("json");
	const apiBaseUrl = env.INKEEP_AGENTS_API_URL;
	const callerId = c.get("userId") ?? "";
	const tenantRole = c.get("tenantRole");
	if (!tenantRole) throw createApiError({
		code: "unauthorized",
		message: "Missing tenant role"
	});
	const id = body.id || generateId();
	const runAsUserId = body.runAsUserId || null;
	if (runAsUserId) {
		if (!callerId) throw createApiError({
			code: "bad_request",
			message: "Authenticated user ID is required when setting runAsUserId"
		});
		await validateRunAsUserId({
			runAsUserId,
			callerId,
			tenantId,
			projectId,
			tenantRole
		});
	}
	logger.debug({
		tenantId,
		projectId,
		agentId,
		triggerId: id
	}, "Creating trigger");
	if (body.signingSecretCredentialReferenceId) {
		const credentialRef = await getCredentialReference(db)({
			scopes: {
				tenantId,
				projectId
			},
			id: body.signingSecretCredentialReferenceId
		});
		if (!credentialRef) throw createApiError({
			code: "bad_request",
			message: `Credential reference not found: ${body.signingSecretCredentialReferenceId}`
		});
		if (credentialRef.userId) throw createApiError({
			code: "bad_request",
			message: "Only project-scoped credentials can be attached to triggers. User-scoped credentials are not allowed."
		});
	}
	let hashedAuthentication;
	const authInput = body.authentication;
	if (authInput?.headers && authInput.headers.length > 0) hashedAuthentication = { headers: await hashAuthenticationHeaders(authInput.headers) };
	const trigger = await createTrigger(db)({
		...body,
		id,
		tenantId,
		projectId,
		agentId,
		enabled: body.enabled !== void 0 ? body.enabled : true,
		authentication: hashedAuthentication,
		signatureVerification: body.signatureVerification,
		runAsUserId,
		createdBy: callerId || null
	});
	const { tenantId: _tid, projectId: _pid, agentId: _aid, ...triggerWithoutScopes } = trigger;
	const hasNoAuth = !body.authentication || !body.authentication.headers?.length;
	const hasNoSignatureVerification = !body.signatureVerification;
	const warning = runAsUserId && hasNoAuth && hasNoSignatureVerification ? "This trigger will authenticate on behalf of the specified users. Please configure authentication or signature verification to ensure the trigger is secure." : void 0;
	return c.json({
		data: {
			...triggerWithoutScopes,
			webhookUrl: generateWebhookUrl({
				baseUrl: apiBaseUrl,
				tenantId,
				projectId,
				agentId,
				triggerId: trigger.id
			})
		},
		...warning && { warning }
	}, 201);
});
/**
* Update Trigger
*/
app.openapi(createProtectedRoute({
	method: "patch",
	path: "/{id}",
	summary: "Update Trigger",
	operationId: "update-trigger",
	tags: ["Triggers"],
	permission: requireProjectPermission("edit"),
	request: {
		params: TenantProjectAgentIdParamsSchema,
		body: { content: { "application/json": { schema: TriggerApiUpdateSchema } } }
	},
	responses: {
		200: {
			description: "Trigger updated successfully",
			content: { "application/json": { schema: TriggerWithWebhookUrlWithWarningResponse } }
		},
		...commonGetErrorResponses
	}
}), async (c) => {
	const db = c.get("db");
	const { tenantId, projectId, agentId, id } = c.req.valid("param");
	const body = c.req.valid("json");
	const apiBaseUrl = env.INKEEP_AGENTS_API_URL;
	const callerId = c.get("userId") ?? "";
	const tenantRole = c.get("tenantRole");
	if (!tenantRole) throw createApiError({
		code: "unauthorized",
		message: "Missing tenant role"
	});
	const existingForAuth = await getTriggerById(db)({
		scopes: {
			tenantId,
			projectId,
			agentId
		},
		triggerId: id
	});
	if (!existingForAuth) throw createApiError({
		code: "not_found",
		message: "Trigger not found"
	});
	assertCanMutateTrigger({
		trigger: {
			createdBy: existingForAuth.createdBy ?? null,
			runAsUserId: existingForAuth.runAsUserId ?? null
		},
		callerId,
		tenantRole
	});
	const runAsUserId = body.runAsUserId !== void 0 ? body.runAsUserId || null : void 0;
	if (runAsUserId && runAsUserId !== existingForAuth.runAsUserId) {
		if (!callerId) throw createApiError({
			code: "bad_request",
			message: "Authenticated user ID is required when setting runAsUserId"
		});
		await validateRunAsUserId({
			runAsUserId,
			callerId,
			tenantId,
			projectId,
			tenantRole
		});
	}
	if (!(body.name !== void 0 || body.description !== void 0 || body.enabled !== void 0 || body.inputSchema !== void 0 || body.outputTransform !== void 0 || body.messageTemplate !== void 0 || body.authentication !== void 0 || body.signingSecretCredentialReferenceId !== void 0 || body.signatureVerification !== void 0 || body.runAsUserId !== void 0)) throw createApiError({
		code: "bad_request",
		message: "No fields to update"
	});
	logger.debug({
		tenantId,
		projectId,
		agentId,
		triggerId: id
	}, "Updating trigger");
	if (body.signingSecretCredentialReferenceId) {
		const credentialRef = await getCredentialReference(db)({
			scopes: {
				tenantId,
				projectId
			},
			id: body.signingSecretCredentialReferenceId
		});
		if (!credentialRef) throw createApiError({
			code: "bad_request",
			message: `Credential reference not found: ${body.signingSecretCredentialReferenceId}`
		});
		if (credentialRef.userId) throw createApiError({
			code: "bad_request",
			message: "Only project-scoped credentials can be attached to triggers. User-scoped credentials are not allowed."
		});
	}
	let hashedAuthentication;
	const authInput = body.authentication;
	if (authInput?.headers && authInput.headers.length > 0) {
		const existingAuth = existingForAuth.authentication;
		const hashedHeaders = [];
		for (const header of authInput.headers) if (header.keepExisting) {
			const existingHeader = existingAuth?.headers?.find((h) => h.name === header.name);
			if (existingHeader) hashedHeaders.push({
				name: header.name,
				valueHash: existingHeader.valueHash,
				valuePrefix: existingHeader.valuePrefix
			});
		} else if (header.value) {
			const hashed = await hashAuthenticationHeaders([{
				name: header.name,
				value: header.value
			}]);
			hashedHeaders.push(hashed[0]);
		}
		hashedAuthentication = hashedHeaders.length > 0 ? { headers: hashedHeaders } : void 0;
	} else if (body.authentication !== void 0) hashedAuthentication = body.authentication;
	const updatedTrigger = await updateTrigger(db)({
		scopes: {
			tenantId,
			projectId,
			agentId
		},
		triggerId: id,
		data: {
			...body,
			authentication: hashedAuthentication,
			signatureVerification: body.signatureVerification,
			...runAsUserId !== void 0 && { runAsUserId }
		}
	});
	if (!updatedTrigger) throw createApiError({
		code: "not_found",
		message: "Trigger not found"
	});
	const { tenantId: _tid, projectId: _pid, agentId: _aid, ...triggerWithoutScopes } = updatedTrigger;
	const effectiveRunAsUserId = updatedTrigger.runAsUserId;
	const effectiveAuth = updatedTrigger.authentication;
	const effectiveSigVerification = updatedTrigger.signatureVerification;
	const hasNoAuthAfterUpdate = !effectiveAuth || !effectiveAuth.headers?.length;
	const updateWarning = effectiveRunAsUserId && hasNoAuthAfterUpdate && !effectiveSigVerification ? "This trigger will authenticate on behalf of the specified users. Please configure authentication or signature verification to ensure the trigger is secure." : void 0;
	return c.json({
		data: {
			...triggerWithoutScopes,
			webhookUrl: generateWebhookUrl({
				baseUrl: apiBaseUrl,
				tenantId,
				projectId,
				agentId,
				triggerId: updatedTrigger.id
			})
		},
		...updateWarning && { warning: updateWarning }
	});
});
/**
* Delete Trigger
*/
app.openapi(createProtectedRoute({
	method: "delete",
	path: "/{id}",
	summary: "Delete Trigger",
	operationId: "delete-trigger",
	tags: ["Triggers"],
	permission: requireProjectPermission("edit"),
	request: { params: TenantProjectAgentIdParamsSchema },
	responses: {
		204: { description: "Trigger deleted successfully" },
		...commonGetErrorResponses
	}
}), async (c) => {
	const db = c.get("db");
	const { tenantId, projectId, agentId, id } = c.req.valid("param");
	const callerId = c.get("userId") ?? "";
	const tenantRole = c.get("tenantRole");
	if (!tenantRole) throw createApiError({
		code: "unauthorized",
		message: "Missing tenant role"
	});
	logger.debug({
		tenantId,
		projectId,
		agentId,
		triggerId: id
	}, "Deleting trigger");
	const existing = await getTriggerById(db)({
		scopes: {
			tenantId,
			projectId,
			agentId
		},
		triggerId: id
	});
	if (!existing) throw createApiError({
		code: "not_found",
		message: "Trigger not found"
	});
	assertCanMutateTrigger({
		trigger: {
			createdBy: existing.createdBy ?? null,
			runAsUserId: existing.runAsUserId ?? null
		},
		callerId,
		tenantRole
	});
	await deleteTrigger(db)({
		scopes: {
			tenantId,
			projectId,
			agentId
		},
		triggerId: id
	});
	return c.body(null, 204);
});
/**
* ========================================
* Trigger Invocation Endpoints
* ========================================
*/
const TriggerInvocationQueryParamsSchema = PaginationQueryParamsSchema.merge(DateTimeFilterQueryParamsSchema).extend({ status: TriggerInvocationStatusEnum.optional().describe("Filter by invocation status") }).openapi("TriggerInvocationQueryParams");
/**
* List Trigger Invocations
*/
app.openapi(createProtectedRoute({
	method: "get",
	path: "/{id}/invocations",
	summary: "List Trigger Invocations",
	operationId: "list-trigger-invocations",
	tags: ["Triggers"],
	permission: requireProjectPermission("view"),
	request: {
		params: TenantProjectAgentIdParamsSchema,
		query: TriggerInvocationQueryParamsSchema
	},
	responses: {
		200: {
			description: "List of trigger invocations retrieved successfully",
			content: { "application/json": { schema: TriggerInvocationListResponse } }
		},
		...commonGetErrorResponses
	},
	...speakeasyOffsetLimitPagination
}), async (c) => {
	const { tenantId, projectId, agentId, id: triggerId } = c.req.valid("param");
	const { page, limit, status, from, to } = c.req.valid("query");
	logger.debug({
		tenantId,
		projectId,
		agentId,
		triggerId,
		status,
		from,
		to
	}, "Listing trigger invocations");
	const result = await listTriggerInvocationsPaginated(runDbClient_default)({
		scopes: {
			tenantId,
			projectId,
			agentId
		},
		triggerId,
		pagination: {
			page,
			limit
		},
		filters: {
			status,
			from,
			to
		}
	});
	const dataWithoutScopes = result.data.map((invocation) => {
		const { tenantId: _tid, projectId: _pid, agentId: _aid, ...rest } = invocation;
		return rest;
	});
	return c.json({
		data: dataWithoutScopes,
		pagination: result.pagination
	});
});
/**
* Get Trigger Invocation by ID
*/
app.openapi(createProtectedRoute({
	method: "get",
	path: "/{id}/invocations/{invocationId}",
	summary: "Get Trigger Invocation",
	operationId: "get-trigger-invocation-by-id",
	tags: ["Triggers"],
	permission: requireProjectPermission("view"),
	request: { params: TenantProjectAgentIdParamsSchema.extend({ invocationId: z.string().describe("Trigger Invocation ID") }) },
	responses: {
		200: {
			description: "Trigger invocation found",
			content: { "application/json": { schema: TriggerInvocationResponse } }
		},
		...commonGetErrorResponses
	}
}), async (c) => {
	const { tenantId, projectId, agentId, id: triggerId, invocationId } = c.req.valid("param");
	logger.debug({
		tenantId,
		projectId,
		agentId,
		triggerId,
		invocationId
	}, "Getting trigger invocation");
	const invocation = await getTriggerInvocationById(runDbClient_default)({
		scopes: {
			tenantId,
			projectId,
			agentId
		},
		triggerId,
		invocationId
	});
	if (!invocation) throw createApiError({
		code: "not_found",
		message: "Trigger invocation not found"
	});
	const { tenantId: _tid, projectId: _pid, agentId: _aid, ...invocationWithoutScopes } = invocation;
	return c.json({ data: invocationWithoutScopes });
});
/**
* Rerun Trigger
* Re-executes a trigger with the provided user message (from a previous trace).
*/
app.openapi(createProtectedRoute({
	method: "post",
	path: "/{id}/rerun",
	summary: "Rerun Trigger",
	operationId: "rerun-trigger",
	tags: ["Triggers"],
	permission: requireProjectPermission("use"),
	request: {
		params: TenantProjectAgentIdParamsSchema,
		body: { content: { "application/json": { schema: z.object({
			userMessage: z.string().describe("The user message to send to the agent"),
			messageParts: z.array(PartSchema).optional().describe("Optional structured message parts (from original trace)")
		}) } } }
	},
	responses: {
		202: {
			description: "Trigger rerun accepted and dispatched",
			content: { "application/json": { schema: z.object({
				success: z.boolean(),
				invocationId: z.string(),
				conversationId: z.string()
			}) } }
		},
		409: errorSchemaFactory("conflict", "Trigger is disabled"),
		...commonGetErrorResponses
	}
}), async (c) => {
	const db = c.get("db");
	const resolvedRef = c.get("resolvedRef");
	const { tenantId, projectId, agentId, id: triggerId } = c.req.valid("param");
	const { userMessage, messageParts: rawMessageParts } = c.req.valid("json");
	const callerId = c.get("userId") ?? "";
	const tenantRole = c.get("tenantRole");
	if (!tenantRole) throw createApiError({
		code: "unauthorized",
		message: "Missing tenant role"
	});
	logger.info({
		tenantId,
		projectId,
		agentId,
		triggerId
	}, "Rerunning trigger");
	const trigger = await getTriggerById(db)({
		scopes: {
			tenantId,
			projectId,
			agentId
		},
		triggerId
	});
	if (!trigger) throw createApiError({
		code: "not_found",
		message: "Trigger not found"
	});
	if (trigger.runAsUserId) assertCanMutateTrigger({
		trigger,
		callerId,
		tenantRole
	});
	if (!trigger.enabled) throw createApiError({
		code: "conflict",
		message: "Trigger is disabled"
	});
	const messageParts = rawMessageParts ?? [{
		kind: "text",
		text: userMessage
	}];
	let invocationId;
	let conversationId;
	try {
		({invocationId, conversationId} = await dispatchExecution({
			tenantId,
			projectId,
			agentId,
			triggerId,
			resolvedRef,
			payload: { _rerun: true },
			transformedPayload: void 0,
			messageParts,
			userMessageText: userMessage,
			runAsUserId: trigger.runAsUserId ?? void 0
		}));
	} catch (error) {
		const errorMessage = error instanceof Error ? error.message : String(error);
		const errorStack = error instanceof Error ? error.stack : void 0;
		logger.error({
			err: errorMessage,
			errorStack,
			tenantId,
			projectId,
			agentId,
			triggerId
		}, "Failed to dispatch trigger rerun execution");
		throw createApiError({
			code: "internal_server_error",
			message: `Something went wrong. Please contact support.`
		});
	}
	logger.info({
		tenantId,
		projectId,
		agentId,
		triggerId,
		invocationId,
		conversationId
	}, "Trigger rerun dispatched");
	return c.json({
		success: true,
		invocationId,
		conversationId
	}, 202);
});
var triggers_default = app;

//#endregion
export { triggers_default as default };