import { toolApprovalDecisions } from "../../db/runtime/runtime-schema.js";
import { and, eq, sql } from "drizzle-orm";

//#region src/data-access/runtime/toolApprovalDecisions.ts
const DEFAULT_CLEANUP_AGE_MINUTES = 15;
const DEFAULT_CLEANUP_BATCH_SIZE = 1e3;
const decisionConditions = (scope, toolCallId) => [
	eq(toolApprovalDecisions.tenantId, scope.tenantId),
	eq(toolApprovalDecisions.projectId, scope.projectId),
	eq(toolApprovalDecisions.conversationId, scope.conversationId),
	eq(toolApprovalDecisions.toolCallId, toolCallId)
];
/**
* Persist an approval/denial so the instance running the suspended agent can
* pick it up. Used when the approval request lands on a different instance than
* the one holding the in-memory pending approval. Upserts so a retry/re-submit
* overwrites rather than conflicts.
*/
const recordToolApprovalDecision = (db) => async (params) => {
	await db.insert(toolApprovalDecisions).values({
		tenantId: params.tenantId,
		projectId: params.projectId,
		conversationId: params.conversationId,
		toolCallId: params.toolCallId,
		approved: params.approved,
		reason: params.reason ?? null
	}).onConflictDoUpdate({
		target: [
			toolApprovalDecisions.tenantId,
			toolApprovalDecisions.projectId,
			toolApprovalDecisions.conversationId,
			toolApprovalDecisions.toolCallId
		],
		set: {
			approved: params.approved,
			reason: params.reason ?? null
		}
	});
};
/**
* Atomically read-and-delete a pending decision for a tool call. Returns null
* when none is present. The delete-on-read (DELETE ... RETURNING) guarantees a
* single waiter consumes any given decision exactly once.
*/
const consumeToolApprovalDecision = (db) => async (params) => {
	const [row] = await db.delete(toolApprovalDecisions).where(and(...decisionConditions(params, params.toolCallId))).returning();
	if (!row) return null;
	return {
		approved: row.approved,
		reason: row.reason ?? null
	};
};
/**
* Delete decisions older than the timeout window. Batched to bound statement
* size, mirroring cleanupExpiredStreamChunks.
*/
const cleanupExpiredToolApprovalDecisions = (db) => async (olderThanMinutes = DEFAULT_CLEANUP_AGE_MINUTES, batchSize = DEFAULT_CLEANUP_BATCH_SIZE) => {
	const cutoff = sql`now() - make_interval(mins => ${olderThanMinutes})`;
	let deleted;
	do {
		const batch = await db.select({
			tenantId: toolApprovalDecisions.tenantId,
			projectId: toolApprovalDecisions.projectId,
			conversationId: toolApprovalDecisions.conversationId,
			toolCallId: toolApprovalDecisions.toolCallId
		}).from(toolApprovalDecisions).where(sql`${toolApprovalDecisions.createdAt} < ${cutoff}`).limit(batchSize);
		deleted = batch.length;
		if (deleted > 0) {
			const pks = batch.map((row) => sql`(${row.tenantId}, ${row.projectId}, ${row.conversationId}, ${row.toolCallId})`);
			await db.delete(toolApprovalDecisions).where(sql`(${toolApprovalDecisions.tenantId}, ${toolApprovalDecisions.projectId}, ${toolApprovalDecisions.conversationId}, ${toolApprovalDecisions.toolCallId}) IN (${sql.join(pks, sql`, `)})`);
		}
	} while (deleted >= batchSize);
};

//#endregion
export { cleanupExpiredToolApprovalDecisions, consumeToolApprovalDecision, recordToolApprovalDecision };