import runDbClient_default from "../../../../data/db/runDbClient.js";
import { getLogger as getLogger$1 } from "../../../../logger.js";
import manageDbClient_default from "../../../../data/db/manageDbClient.js";
import "../../../../data/db/index.js";
import { buildTimezoneHeaders, executeAgentAsync } from "../../services/TriggerService.js";
import { addConversationIdToInvocation, advanceScheduledTriggerNextRunAt, canUseProjectStrict, createScheduledTriggerInvocation, generateId, getProjectScopedRef, getScheduledTriggerById, getScheduledTriggerInvocationById, getScheduledTriggerInvocationByIdempotencyKey, interpolateTemplate, markScheduledTriggerInvocationCompleted, markScheduledTriggerInvocationFailed, markScheduledTriggerInvocationRunning, resetCancelledInvocationToPending, resolveRef, updateScheduledTriggerInvocationStatus } from "@inkeep/agents-core";

//#region src/domains/run/workflow/steps/scheduledTriggerSteps.ts
/**
* Step functions for scheduled trigger workflow.
*
* These step functions have full Node.js access and handle all database
* operations and external service calls.
*/
const logger = getLogger$1("workflow-scheduled-trigger-steps");
/**
* Step: Log a message (allows logging from workflow context)
*/
async function logStep(message, data) {
	"use step";
	logger.info(data, message);
}
/**
* Step: Check if trigger is still enabled.
* Reads trigger config from the runtime DB.
*/
async function checkTriggerEnabledStep(params) {
	"use step";
	const scopes = {
		tenantId: params.tenantId,
		projectId: params.projectId,
		agentId: params.agentId
	};
	const trigger = await getScheduledTriggerById(runDbClient_default)({
		scopes,
		scheduledTriggerId: params.scheduledTriggerId
	});
	if (!trigger || !trigger.enabled) {
		logger.info({
			scheduledTriggerId: params.scheduledTriggerId,
			reason: !trigger ? "deleted" : "disabled"
		}, "Scheduled trigger workflow stopping");
		return {
			shouldContinue: false,
			reason: !trigger ? "deleted" : "disabled",
			trigger: null
		};
	}
	return {
		shouldContinue: true,
		trigger
	};
}
/**
* Step: Try to create invocation idempotently.
* Returns existing invocation if already created.
*/
async function createInvocationIdempotentStep(params) {
	"use step";
	const existing = await getScheduledTriggerInvocationByIdempotencyKey(runDbClient_default)({ idempotencyKey: params.idempotencyKey });
	if (existing) {
		logger.info({
			scheduledTriggerId: params.scheduledTriggerId,
			idempotencyKey: params.idempotencyKey
		}, "Invocation already exists, skipping creation");
		return {
			invocation: existing,
			alreadyExists: true
		};
	}
	const projectScopedRef = getProjectScopedRef(params.tenantId, params.projectId, params.ref);
	const resolvedRef = await resolveRef(manageDbClient_default)(projectScopedRef);
	if (!resolvedRef) logger.warn({
		tenantId: params.tenantId,
		projectId: params.projectId
	}, "Failed to resolve ref for project, run will not be associated with a branch");
	const invocationId = generateId();
	const invocation = await createScheduledTriggerInvocation(runDbClient_default)({
		id: invocationId,
		tenantId: params.tenantId,
		projectId: params.projectId,
		agentId: params.agentId,
		scheduledTriggerId: params.scheduledTriggerId,
		ref: resolvedRef ?? void 0,
		status: "pending",
		scheduledFor: params.scheduledFor,
		resolvedPayload: params.payload,
		idempotencyKey: params.idempotencyKey,
		attemptNumber: 1,
		runAsUserId: params.runAsUserId
	});
	logger.info({
		tenantId: params.tenantId,
		projectId: params.projectId,
		scheduledTriggerId: params.scheduledTriggerId,
		invocationId,
		scheduledFor: params.scheduledFor
	}, "Created scheduled trigger invocation");
	return {
		invocation,
		alreadyExists: false
	};
}
/**
* Step: Check if invocation was cancelled before execution
* Returns true if cancelled (should skip execution), false otherwise
*/
async function checkInvocationCancelledStep(params) {
	"use step";
	const invocation = await getScheduledTriggerInvocationById(runDbClient_default)({
		scopes: {
			tenantId: params.tenantId,
			projectId: params.projectId,
			agentId: params.agentId
		},
		scheduledTriggerId: params.scheduledTriggerId,
		invocationId: params.invocationId
	});
	if (!invocation) {
		logger.warn({
			scheduledTriggerId: params.scheduledTriggerId,
			invocationId: params.invocationId
		}, "Invocation not found when checking cancellation status");
		return { cancelled: true };
	}
	if (invocation.status === "cancelled") {
		logger.info({
			scheduledTriggerId: params.scheduledTriggerId,
			invocationId: params.invocationId
		}, "Invocation was cancelled, skipping execution");
		return { cancelled: true };
	}
	return { cancelled: false };
}
/**
* Step: Mark invocation as running
*/
async function markRunningStep(params) {
	"use step";
	logger.info({
		scheduledTriggerId: params.scheduledTriggerId,
		invocationId: params.invocationId
	}, "Marking invocation as running");
	return markScheduledTriggerInvocationRunning(runDbClient_default)({
		scopes: {
			tenantId: params.tenantId,
			projectId: params.projectId,
			agentId: params.agentId
		},
		scheduledTriggerId: params.scheduledTriggerId,
		invocationId: params.invocationId
	});
}
/**
* Step: Add a conversation ID to the invocation's conversationIds array
* Called after each attempt to track all conversations created during retries
*/
async function addConversationIdStep(params) {
	"use step";
	return addConversationIdToInvocation(runDbClient_default)({
		scopes: {
			tenantId: params.tenantId,
			projectId: params.projectId,
			agentId: params.agentId
		},
		scheduledTriggerId: params.scheduledTriggerId,
		invocationId: params.invocationId,
		conversationId: params.conversationId
	});
}
/**
* Step: Mark invocation as completed
*/
async function markCompletedStep(params) {
	"use step";
	return markScheduledTriggerInvocationCompleted(runDbClient_default)({
		scopes: {
			tenantId: params.tenantId,
			projectId: params.projectId,
			agentId: params.agentId
		},
		scheduledTriggerId: params.scheduledTriggerId,
		invocationId: params.invocationId
	});
}
/**
* Step: Mark invocation as failed
*/
async function markFailedStep(params) {
	"use step";
	return markScheduledTriggerInvocationFailed(runDbClient_default)({
		scopes: {
			tenantId: params.tenantId,
			projectId: params.projectId,
			agentId: params.agentId
		},
		scheduledTriggerId: params.scheduledTriggerId,
		invocationId: params.invocationId
	});
}
async function disableOneTimeTriggerStep(params) {
	"use step";
	const trigger = await getScheduledTriggerById(runDbClient_default)({
		scopes: {
			tenantId: params.tenantId,
			projectId: params.projectId,
			agentId: params.agentId
		},
		scheduledTriggerId: params.scheduledTriggerId
	});
	if (!trigger || !trigger.enabled) return;
	await advanceScheduledTriggerNextRunAt(runDbClient_default)({
		scopes: {
			tenantId: params.tenantId,
			projectId: params.projectId,
			agentId: params.agentId
		},
		scheduledTriggerId: params.scheduledTriggerId,
		nextRunAt: null,
		enabled: false
	});
}
/**
* Step: Increment attempt number for retry
*/
async function incrementAttemptStep(params) {
	"use step";
	await updateScheduledTriggerInvocationStatus(runDbClient_default)({
		scopes: {
			tenantId: params.tenantId,
			projectId: params.projectId,
			agentId: params.agentId
		},
		scheduledTriggerId: params.scheduledTriggerId,
		invocationId: params.invocationId,
		data: {
			attemptNumber: params.currentAttempt + 1,
			status: "pending"
		}
	});
}
/**
* Step: Reset a cancelled invocation back to pending.
* Used when a restarted workflow finds a cancelled invocation via idempotency
* that is still scheduled for a future time.
*/
async function resetInvocationToPendingStep(params) {
	"use step";
	const updated = await resetCancelledInvocationToPending(runDbClient_default)({
		scopes: {
			tenantId: params.tenantId,
			projectId: params.projectId,
			agentId: params.agentId
		},
		scheduledTriggerId: params.scheduledTriggerId,
		invocationId: params.invocationId
	});
	if (updated) logger.info({
		scheduledTriggerId: params.scheduledTriggerId,
		invocationId: params.invocationId
	}, "Reset cancelled invocation to pending");
	else logger.warn({
		scheduledTriggerId: params.scheduledTriggerId,
		invocationId: params.invocationId
	}, "Skipped reset — invocation status changed concurrently (no longer cancelled)");
	return updated;
}
/**
* Step: Execute the scheduled trigger using executeAgentAsync.
*
* Uses the shared executeAgentAsync from TriggerService which includes
* proper tracing, error handling, and conversation management.
*/
async function executeScheduledTriggerStep(params) {
	"use step";
	const { tenantId, projectId, agentId, scheduledTriggerId, invocationId, messageTemplate, payload, timeoutSeconds, runAsUserId, cronTimezone, ref: triggerRef } = params;
	if (runAsUserId) try {
		if (!await canUseProjectStrict({
			userId: runAsUserId,
			tenantId,
			projectId
		})) {
			logger.warn({
				scheduledTriggerId,
				invocationId,
				runAsUserId,
				projectId
			}, "User no longer has access to project, failing invocation");
			return {
				success: false,
				error: `User ${runAsUserId} no longer has 'use' permission on project ${projectId}. An org admin should update or remove the runAsUserId on this trigger.`
			};
		}
	} catch (err) {
		logger.error({
			scheduledTriggerId,
			invocationId,
			runAsUserId,
			projectId,
			error: err
		}, "Failed to check user project access");
		return {
			success: false,
			error: `Permission check failed for user ${runAsUserId}: ${err instanceof Error ? err.message : String(err)}`
		};
	}
	logger.info({
		scheduledTriggerId,
		invocationId,
		runAsUserId,
		ref: triggerRef
	}, "Executing scheduled trigger via executeAgentAsync");
	const conversationId = generateId();
	try {
		const refString = getProjectScopedRef(tenantId, projectId, triggerRef);
		const resolvedRef = await resolveRef(manageDbClient_default)(refString);
		if (!resolvedRef) return {
			success: false,
			conversationId,
			error: `Failed to resolve ref '${triggerRef}' for project ${projectId}`
		};
		const effectivePayload = payload ?? {};
		let userMessage;
		if (messageTemplate) userMessage = interpolateTemplate(messageTemplate, effectivePayload);
		else userMessage = JSON.stringify(effectivePayload);
		const messageParts = [];
		if (messageTemplate) messageParts.push({
			kind: "text",
			text: userMessage
		});
		messageParts.push({
			kind: "data",
			data: effectivePayload,
			metadata: {
				source: "scheduled-trigger",
				triggerId: scheduledTriggerId
			}
		});
		const timeoutMs = timeoutSeconds * 1e3;
		const timeoutPromise = new Promise((_, reject) => {
			setTimeout(() => reject(/* @__PURE__ */ new Error(`Execution timed out after ${timeoutSeconds}s`)), timeoutMs);
		});
		await Promise.race([executeAgentAsync({
			tenantId,
			projectId,
			agentId,
			triggerId: scheduledTriggerId,
			invocationId,
			conversationId,
			userMessage,
			messageParts,
			resolvedRef,
			runAsUserId: runAsUserId ?? void 0,
			forwardedHeaders: buildTimezoneHeaders(cronTimezone),
			invocationType: "scheduled_trigger"
		}), timeoutPromise]);
		logger.info({
			scheduledTriggerId,
			invocationId,
			conversationId
		}, "Scheduled trigger execution completed");
		return {
			success: true,
			conversationId
		};
	} catch (error) {
		const errorMessage = error instanceof Error ? error.message : String(error);
		logger.error({
			scheduledTriggerId,
			invocationId,
			conversationId,
			error: errorMessage
		}, "Execute scheduled trigger step failed");
		return {
			success: false,
			conversationId,
			error: errorMessage
		};
	}
}

//#endregion
export { addConversationIdStep, checkInvocationCancelledStep, checkTriggerEnabledStep, createInvocationIdempotentStep, disableOneTimeTriggerStep, executeScheduledTriggerStep, incrementAttemptStep, logStep, markCompletedStep, markFailedStep, markRunningStep, resetInvocationToPendingStep };