import { env } from "../../../env.js";
import manageDbClient_default from "../../../data/db/manageDbClient.js";
import manageDbPool_default from "../../../data/db/manageDbPool.js";
import { getLogger as getLogger$1 } from "../../../logger.js";
import "../../../data/db/index.js";
import { CONVERSATION_DETAIL_MESSAGE_LIMIT, formatConversationDetail, formatEvent, formatFeedback } from "../../../utils/conversationFormatter.js";
import { webhookDeliveryWorkflow } from "../workflow/functions/webhookDelivery.js";
import { buildSlackPayload, isSlackIncomingWebhookUrl } from "./slackBlockKit.js";
import { dispatchViaQueue } from "./webhookQueueDispatcher.js";
import { getConversation, getConversationHistory, getEvaluationRunById, getProjectMainResolvedRef, getWaitUntil, listWebhookDestinationsForEvent, withRef } from "@inkeep/agents-core";
import { start } from "workflow/api";

//#region src/domains/run/services/WebhookDeliveryService.ts
const logger = getLogger$1("WebhookDeliveryService");
const useQueue = !!process.env.VERCEL;
async function emitConversationWebhook(params) {
	const { runDbClient: db, tenantId, projectId, agentId, conversationId, resolvedRef, eventType } = params;
	const scopes = {
		tenantId,
		projectId
	};
	const promise = Promise.all([getConversation(db)({
		scopes,
		conversationId
	}), getConversationHistory(db)({
		scopes,
		conversationId,
		options: { limit: CONVERSATION_DETAIL_MESSAGE_LIMIT }
	})]).then(([conversation, messages]) => {
		if (!conversation) {
			logger.warn({
				conversationId,
				eventType
			}, "Skipping webhook emit: conversation not found");
			return;
		}
		return emitWebhookEvent({
			tenantId,
			projectId,
			agentId,
			resolvedRef,
			eventType,
			data: { conversation: formatConversationDetail(conversation, messages) }
		});
	}).catch((err) => {
		logger.warn({
			error: err instanceof Error ? err.message : String(err),
			conversationId
		}, `Failed to emit ${eventType} webhook event`);
	});
	const waitUntil = await getWaitUntil();
	if (waitUntil) waitUntil(promise);
}
async function emitWebhookEvent(params) {
	const { tenantId, projectId, agentId, resolvedRef, eventType, data, slackMeta } = params;
	let destinations = [];
	try {
		await withRef(manageDbPool_default, resolvedRef, async (db) => {
			destinations = await listWebhookDestinationsForEvent(db)({
				scopes: {
					tenantId,
					projectId
				},
				eventType,
				agentId
			});
		});
	} catch (err) {
		logger.error({
			tenantId,
			projectId,
			agentId,
			eventType,
			error: err instanceof Error ? err.message : String(err)
		}, "Failed to query webhook destinations");
		return;
	}
	if (destinations.length === 0) return;
	const envelope = {
		type: eventType,
		timestamp: (/* @__PURE__ */ new Date()).toISOString(),
		tenantId,
		projectId,
		agentId,
		data
	};
	const slackCtx = {
		tenantId,
		projectId,
		agentId,
		manageUiBaseUrl: env.INKEEP_AGENTS_MANAGE_UI_URL || "http://localhost:3000"
	};
	const payloads = destinations.map((dest) => {
		const isSlackWebhook = isSlackIncomingWebhookUrl(dest.url);
		let payload;
		if (isSlackWebhook) try {
			payload = buildSlackPayload(eventType, envelope, slackCtx, slackMeta);
		} catch (err) {
			logger.error({
				webhookDestinationId: dest.id,
				eventType,
				error: err instanceof Error ? err.message : String(err)
			}, "Failed to build Slack payload, falling back to raw envelope");
			payload = envelope;
		}
		else payload = envelope;
		return {
			destinationUrl: dest.url,
			tenantId,
			projectId,
			agentId,
			webhookDestinationId: dest.id,
			payload,
			headers: dest.headers
		};
	});
	const results = await Promise.allSettled(payloads.map((deliveryPayload) => useQueue ? dispatchViaQueue(deliveryPayload) : start(webhookDeliveryWorkflow, [deliveryPayload])));
	let dispatched = 0;
	for (let i = 0; i < results.length; i++) if (results[i].status === "fulfilled") dispatched++;
	else {
		const error = results[i].reason;
		logger.error({
			webhookDestinationId: destinations[i].id,
			eventType,
			error: error instanceof Error ? error.message : String(error)
		}, useQueue ? "Failed to enqueue webhook delivery" : "Failed to start webhook delivery workflow");
	}
	if (dispatched > 0) logger.debug({
		tenantId,
		projectId,
		agentId,
		eventType,
		dispatched,
		total: destinations.length
	}, useQueue ? "Webhook deliveries enqueued" : "Webhook delivery workflows started");
}
function emitWebhookEventFireAndForget(params, context) {
	getWaitUntil().then((waitUntil) => {
		const promise = emitWebhookEvent(params).catch((err) => {
			logger.warn({
				error: err instanceof Error ? err.message : String(err),
				eventType: params.eventType
			}, `Failed to emit ${params.eventType} webhook event (${context})`);
		});
		if (waitUntil) waitUntil(promise);
	}).catch((err) => {
		logger.warn({
			error: err instanceof Error ? err.message : String(err),
			eventType: params.eventType
		}, `Unexpected error in webhook fire-and-forget setup (${context})`);
	});
}
async function emitFeedbackWebhook(params) {
	const { runDbClient: db, tenantId, projectId, agentId, feedback } = params;
	const scopes = {
		tenantId,
		projectId
	};
	const promise = Promise.all([
		getProjectMainResolvedRef(manageDbClient_default)(tenantId, projectId),
		getConversation(db)({
			scopes,
			conversationId: feedback.conversationId
		}),
		getConversationHistory(db)({
			scopes,
			conversationId: feedback.conversationId,
			options: { limit: CONVERSATION_DETAIL_MESSAGE_LIMIT }
		})
	]).then(([resolvedRef, conversation, messages]) => {
		if (!conversation) {
			logger.warn({
				conversationId: feedback.conversationId,
				feedbackId: feedback.id
			}, "Skipping feedback webhook emit: conversation not found");
			return;
		}
		return emitWebhookEvent({
			tenantId,
			projectId,
			agentId: agentId ?? conversation.agentId ?? "",
			resolvedRef,
			eventType: "feedback.created",
			data: {
				feedback: formatFeedback(feedback),
				conversation: formatConversationDetail(conversation, messages)
			}
		});
	}).catch((err) => {
		logger.warn({
			error: err instanceof Error ? err.message : String(err),
			feedbackId: feedback.id
		}, "Failed to emit feedback.created webhook event");
	});
	const waitUntil = await getWaitUntil();
	if (waitUntil) waitUntil(promise);
}
async function emitEventWebhook(params) {
	const { tenantId, projectId, agentId, resolvedRef, event } = params;
	const promise = emitWebhookEvent({
		tenantId,
		projectId,
		agentId,
		resolvedRef,
		eventType: "event.created",
		data: { event: formatEvent(event) }
	}).catch((err) => {
		logger.warn({
			tenantId,
			projectId,
			eventId: event.id,
			type: event.type,
			error: err instanceof Error ? err.message : String(err)
		}, "Failed to emit event.created webhook event");
	});
	const waitUntil = await getWaitUntil();
	if (waitUntil) waitUntil(promise);
}
async function emitEvaluationFailedWebhook(params) {
	const { runDbClient: db, tenantId, projectId, verdict, failedConditions, evaluationResult, evaluator } = params;
	const scopes = {
		tenantId,
		projectId
	};
	if (verdict !== "failed") return;
	const evaluationRunPromise = evaluationResult.evaluationRunId ? getEvaluationRunById(db)({ scopes: {
		tenantId,
		projectId,
		evaluationRunId: evaluationResult.evaluationRunId
	} }) : Promise.resolve(null);
	const promise = Promise.all([
		getProjectMainResolvedRef(manageDbClient_default)(tenantId, projectId),
		getConversation(db)({
			scopes,
			conversationId: evaluationResult.conversationId
		}),
		evaluationRunPromise
	]).then(([resolvedRef, conversation, evaluationRun]) => {
		if (!conversation) {
			logger.warn({
				conversationId: evaluationResult.conversationId,
				evaluationResultId: evaluationResult.id
			}, "Skipping evaluation.failed webhook emit: conversation not found");
			return;
		}
		return emitWebhookEvent({
			tenantId,
			projectId,
			agentId: conversation.agentId ?? "",
			resolvedRef,
			eventType: "evaluation.failed",
			data: {
				evaluator: {
					id: evaluator.id,
					name: evaluator.name
				},
				conversation: { id: evaluationResult.conversationId },
				failedConditions
			},
			slackMeta: {
				evaluationRunConfigId: evaluationRun?.evaluationRunConfigId ?? null,
				evaluationJobConfigId: evaluationRun?.evaluationJobConfigId ?? null
			}
		});
	}).catch((err) => {
		logger.warn({
			error: err instanceof Error ? err.message : String(err),
			evaluationResultId: evaluationResult.id,
			evaluatorId: evaluationResult.evaluatorId,
			conversationId: evaluationResult.conversationId,
			evaluationRunId: evaluationResult.evaluationRunId
		}, "Failed to emit evaluation.failed webhook event");
	});
	const waitUntil = await getWaitUntil();
	if (waitUntil) waitUntil(promise);
}

//#endregion
export { emitConversationWebhook, emitEvaluationFailedWebhook, emitEventWebhook, emitFeedbackWebhook, emitWebhookEvent, emitWebhookEventFireAndForget };