import { apiKeys, contextCache, conversations, datasetRun, evaluationRun, events, scheduledTriggerInvocations, scheduledTriggers, tasks, triggerInvocations, workAppGitHubMcpToolAccessMode, workAppGitHubMcpToolRepositoryAccess, workAppGitHubProjectAccessMode, workAppGitHubProjectRepositoryAccess } from "../../db/runtime/runtime-schema.js";
import { projectScopedWhere } from "../manage/scope-helpers.js";
import { clearAppDefaultsByAgent, clearAppDefaultsByProject, deleteAppsByProject } from "./apps.js";
import { deleteEventsByConversationIds } from "./events.js";
import { deleteSlackMcpToolAccessConfig } from "./slack-work-app-mcp.js";
import { clearDevConfigWorkspaceDefaultsByAgent, clearDevConfigWorkspaceDefaultsByProject, clearWorkspaceDefaultsByAgent, clearWorkspaceDefaultsByProject, deleteWorkAppSlackChannelAgentConfigsByAgent, deleteWorkAppSlackChannelAgentConfigsByProject } from "./workAppSlack.js";
import { and, eq, inArray, sql } from "drizzle-orm";

//#region src/data-access/runtime/cascade-delete.ts
/**
* Delete all runtime entities for a specific branch.
* PostgreSQL cascades handle: messages, taskRelations, ledgerArtifacts.
* Events are deleted explicitly before conversations (no FK cascade).
*
* @param db - Runtime database client
* @returns Function that performs the cascade delete
*/
const cascadeDeleteByBranch = (db) => async (params) => {
	const { scopes, fullBranchName, ref } = params;
	const contextCacheResult = await db.delete(contextCache).where(and(projectScopedWhere(contextCache, scopes), sql`${contextCache.ref}->>'name' = ${fullBranchName}`)).returning();
	const branchConversationIds = (await db.select({ id: conversations.id }).from(conversations).where(and(projectScopedWhere(conversations, scopes), sql`${conversations.ref}->>'name' = ${fullBranchName}`))).map((c) => c.id);
	const eventsDeleted = await deleteEventsByConversationIds(db)({
		scopes,
		conversationIds: branchConversationIds
	});
	const conversationsResult = await db.delete(conversations).where(and(projectScopedWhere(conversations, scopes), sql`${conversations.ref}->>'name' = ${fullBranchName}`)).returning();
	const tasksResult = await db.delete(tasks).where(and(projectScopedWhere(tasks, scopes), sql`${tasks.ref}->>'name' = ${fullBranchName}`)).returning();
	const triggerInvocationsResult = await db.delete(triggerInvocations).where(and(eq(triggerInvocations.tenantId, scopes.tenantId), eq(triggerInvocations.projectId, scopes.projectId), sql`${triggerInvocations.ref}->>'name' = ${fullBranchName}`)).returning();
	const scheduledTriggerInvocationsResult = await db.delete(scheduledTriggerInvocations).where(and(eq(scheduledTriggerInvocations.tenantId, scopes.tenantId), eq(scheduledTriggerInvocations.projectId, scopes.projectId), sql`${scheduledTriggerInvocations.ref}->>'name' = ${fullBranchName}`)).returning();
	const datasetRunsResult = await db.delete(datasetRun).where(and(eq(datasetRun.tenantId, scopes.tenantId), eq(datasetRun.projectId, scopes.projectId), sql`${datasetRun.ref}->>'name' = ${fullBranchName}`)).returning();
	const evaluationRunsResult = await db.delete(evaluationRun).where(and(eq(evaluationRun.tenantId, scopes.tenantId), eq(evaluationRun.projectId, scopes.projectId), sql`${evaluationRun.ref}->>'name' = ${fullBranchName}`)).returning();
	const scheduledTriggersResult = await db.delete(scheduledTriggers).where(and(eq(scheduledTriggers.tenantId, scopes.tenantId), eq(scheduledTriggers.projectId, scopes.projectId), eq(scheduledTriggers.ref, ref))).returning();
	return {
		conversationsDeleted: conversationsResult.length,
		tasksDeleted: tasksResult.length,
		contextCacheDeleted: contextCacheResult.length,
		triggerInvocationsDeleted: triggerInvocationsResult.length,
		scheduledTriggerInvocationsDeleted: scheduledTriggerInvocationsResult.length,
		datasetRunsDeleted: datasetRunsResult.length,
		evaluationRunsDeleted: evaluationRunsResult.length,
		apiKeysDeleted: 0,
		slackChannelConfigsDeleted: 0,
		slackWorkspaceDefaultsCleared: 0,
		appsDeleted: 0,
		appDefaultsCleared: 0,
		scheduledTriggersDeleted: scheduledTriggersResult.length,
		eventsDeleted
	};
};
/**
* Delete all runtime entities for a project across all branches.
* Used when deleting a project entirely.
* PostgreSQL cascades handle: messages, taskRelations, ledgerArtifacts.
* Events are deleted explicitly before conversations (no FK cascade).
*
* @param db - Runtime database client
* @returns Function that performs the cascade delete
*/
const cascadeDeleteByProject = (db) => async (params) => {
	const { scopes } = params;
	const contextCacheResult = await db.delete(contextCache).where(projectScopedWhere(contextCache, scopes)).returning();
	const eventsResult = await db.delete(events).where(projectScopedWhere(events, scopes)).returning();
	const conversationsResult = await db.delete(conversations).where(projectScopedWhere(conversations, scopes)).returning();
	const tasksResult = await db.delete(tasks).where(projectScopedWhere(tasks, scopes)).returning();
	const triggerInvocationsResult = await db.delete(triggerInvocations).where(and(eq(triggerInvocations.tenantId, scopes.tenantId), eq(triggerInvocations.projectId, scopes.projectId))).returning();
	const scheduledTriggerInvocationsResult = await db.delete(scheduledTriggerInvocations).where(and(eq(scheduledTriggerInvocations.tenantId, scopes.tenantId), eq(scheduledTriggerInvocations.projectId, scopes.projectId))).returning();
	const datasetRunsResult = await db.delete(datasetRun).where(and(eq(datasetRun.tenantId, scopes.tenantId), eq(datasetRun.projectId, scopes.projectId))).returning();
	const evaluationRunsResult = await db.delete(evaluationRun).where(and(eq(evaluationRun.tenantId, scopes.tenantId), eq(evaluationRun.projectId, scopes.projectId))).returning();
	const apiKeysResult = await db.delete(apiKeys).where(projectScopedWhere(apiKeys, scopes)).returning();
	await cascadeDeleteGitHubAccessByProject(db)({
		tenantId: scopes.tenantId,
		projectId: scopes.projectId
	});
	const appsDeleted = await deleteAppsByProject(db)(scopes.tenantId, scopes.projectId);
	const appDefaultsCleared = await clearAppDefaultsByProject(db)(scopes.tenantId, scopes.projectId);
	const slackChannelConfigsDeleted = await deleteWorkAppSlackChannelAgentConfigsByProject(db)(scopes.tenantId, scopes.projectId);
	const slackWorkspaceDefaultsCleared = await clearWorkspaceDefaultsByProject(db)(scopes.tenantId, scopes.projectId);
	clearDevConfigWorkspaceDefaultsByProject(scopes.projectId);
	const scheduledTriggersResult = await db.delete(scheduledTriggers).where(and(eq(scheduledTriggers.tenantId, scopes.tenantId), eq(scheduledTriggers.projectId, scopes.projectId))).returning();
	return {
		conversationsDeleted: conversationsResult.length,
		tasksDeleted: tasksResult.length,
		contextCacheDeleted: contextCacheResult.length,
		triggerInvocationsDeleted: triggerInvocationsResult.length,
		scheduledTriggerInvocationsDeleted: scheduledTriggerInvocationsResult.length,
		datasetRunsDeleted: datasetRunsResult.length,
		evaluationRunsDeleted: evaluationRunsResult.length,
		apiKeysDeleted: apiKeysResult.length,
		slackChannelConfigsDeleted,
		slackWorkspaceDefaultsCleared,
		appsDeleted,
		appDefaultsCleared,
		scheduledTriggersDeleted: scheduledTriggersResult.length,
		eventsDeleted: eventsResult.length
	};
};
/**
* Delete all runtime entities for a specific agent on a specific branch.
* This includes tasks for the agent and conversations where the agent's
* subAgents are active.
*
* @param db - Runtime database client
* @returns Function that performs the cascade delete
*/
const cascadeDeleteByAgent = (db) => async (params) => {
	const { scopes, fullBranchName, subAgentIds } = params;
	let contextCacheDeleted = 0;
	let conversationsDeleted = 0;
	let tasksDeleted = 0;
	let apiKeysDeleted = 0;
	let eventsDeleted = 0;
	if (subAgentIds.length > 0) {
		const conversationIds = (await db.select({ id: conversations.id }).from(conversations).where(and(projectScopedWhere(conversations, scopes), inArray(conversations.activeSubAgentId, subAgentIds), sql`${conversations.ref}->>'name' = ${fullBranchName}`))).map((c) => c.id);
		if (conversationIds.length > 0) {
			contextCacheDeleted = (await db.delete(contextCache).where(and(projectScopedWhere(contextCache, scopes), inArray(contextCache.conversationId, conversationIds))).returning()).length;
			eventsDeleted = await deleteEventsByConversationIds(db)({
				scopes,
				conversationIds
			});
			conversationsDeleted = (await db.delete(conversations).where(and(projectScopedWhere(conversations, scopes), inArray(conversations.id, conversationIds))).returning()).length;
		}
	}
	tasksDeleted = (await db.delete(tasks).where(and(projectScopedWhere(tasks, scopes), eq(tasks.agentId, scopes.agentId), sql`${tasks.ref}->>'name' = ${fullBranchName}`)).returning()).length;
	const triggerInvocationsResult = await db.delete(triggerInvocations).where(and(eq(triggerInvocations.tenantId, scopes.tenantId), eq(triggerInvocations.projectId, scopes.projectId), eq(triggerInvocations.agentId, scopes.agentId), sql`${triggerInvocations.ref}->>'name' = ${fullBranchName}`)).returning();
	const scheduledTriggerInvocationsResult = await db.delete(scheduledTriggerInvocations).where(and(eq(scheduledTriggerInvocations.tenantId, scopes.tenantId), eq(scheduledTriggerInvocations.projectId, scopes.projectId), eq(scheduledTriggerInvocations.agentId, scopes.agentId), sql`${scheduledTriggerInvocations.ref}->>'name' = ${fullBranchName}`)).returning();
	apiKeysDeleted = (await db.delete(apiKeys).where(and(projectScopedWhere(apiKeys, scopes), eq(apiKeys.agentId, scopes.agentId))).returning()).length;
	const appDefaultsCleared = await clearAppDefaultsByAgent(db)(scopes.tenantId, scopes.agentId);
	const slackChannelConfigsDeleted = await deleteWorkAppSlackChannelAgentConfigsByAgent(db)(scopes.tenantId, scopes.projectId, scopes.agentId);
	const slackWorkspaceDefaultsCleared = await clearWorkspaceDefaultsByAgent(db)(scopes.tenantId, scopes.projectId, scopes.agentId);
	clearDevConfigWorkspaceDefaultsByAgent(scopes.projectId, scopes.agentId);
	const scheduledTriggersResult = await db.delete(scheduledTriggers).where(and(eq(scheduledTriggers.tenantId, scopes.tenantId), eq(scheduledTriggers.projectId, scopes.projectId), eq(scheduledTriggers.agentId, scopes.agentId))).returning();
	return {
		conversationsDeleted,
		tasksDeleted,
		contextCacheDeleted,
		triggerInvocationsDeleted: triggerInvocationsResult.length,
		scheduledTriggerInvocationsDeleted: scheduledTriggerInvocationsResult.length,
		datasetRunsDeleted: 0,
		evaluationRunsDeleted: 0,
		apiKeysDeleted,
		slackChannelConfigsDeleted,
		slackWorkspaceDefaultsCleared,
		appsDeleted: 0,
		appDefaultsCleared,
		scheduledTriggersDeleted: scheduledTriggersResult.length,
		eventsDeleted
	};
};
/**
* Delete all runtime entities for a specific subAgent on a specific branch.
*
* @param db - Runtime database client
* @returns Function that performs the cascade delete
*/
const cascadeDeleteBySubAgent = (db) => async (params) => {
	const { scopes, subAgentId, fullBranchName } = params;
	const conversationIds = (await db.select({ id: conversations.id }).from(conversations).where(and(projectScopedWhere(conversations, scopes), eq(conversations.activeSubAgentId, subAgentId), sql`${conversations.ref}->>'name' = ${fullBranchName}`))).map((c) => c.id);
	let contextCacheDeleted = 0;
	let conversationsDeleted = 0;
	let eventsDeleted = 0;
	if (conversationIds.length > 0) {
		contextCacheDeleted = (await db.delete(contextCache).where(and(projectScopedWhere(contextCache, scopes), inArray(contextCache.conversationId, conversationIds))).returning()).length;
		eventsDeleted = await deleteEventsByConversationIds(db)({
			scopes,
			conversationIds
		});
		conversationsDeleted = (await db.delete(conversations).where(and(projectScopedWhere(conversations, scopes), inArray(conversations.id, conversationIds))).returning()).length;
	}
	const tasksResult = await db.delete(tasks).where(and(projectScopedWhere(tasks, scopes), eq(tasks.subAgentId, subAgentId), sql`${tasks.ref}->>'name' = ${fullBranchName}`)).returning();
	return {
		conversationsDeleted,
		tasksDeleted: tasksResult.length,
		contextCacheDeleted,
		triggerInvocationsDeleted: 0,
		scheduledTriggerInvocationsDeleted: 0,
		datasetRunsDeleted: 0,
		evaluationRunsDeleted: 0,
		apiKeysDeleted: 0,
		slackChannelConfigsDeleted: 0,
		slackWorkspaceDefaultsCleared: 0,
		appsDeleted: 0,
		appDefaultsCleared: 0,
		scheduledTriggersDeleted: 0,
		eventsDeleted
	};
};
/**
* Delete contextCache entries for a specific contextConfig.
* Used when deleting a contextConfig from the manage DB.
*
* @param db - Runtime database client
* @returns Function that performs the delete
*/
const cascadeDeleteByContextConfig = (db) => async (params) => {
	const { scopes, contextConfigId, fullBranchName } = params;
	return { contextCacheDeleted: (await db.delete(contextCache).where(and(projectScopedWhere(contextCache, scopes), eq(contextCache.contextConfigId, contextConfigId), sql`${contextCache.ref}->>'name' = ${fullBranchName}`)).returning()).length };
};
/**
* Delete all runtime entities for a specific MCP tool.
* Called when an MCP tool is deleted from the manage database.
*
* Cleans up:
* - workAppGitHubMcpToolRepositoryAccess entries
* - workAppGitHubMcpToolAccessMode entry
*
* @param db - Runtime database client
* @returns Function that performs the cascade delete
*/
const cascadeDeleteByTool = (db) => async (params) => {
	const { toolId, tenantId, projectId } = params;
	const scopes = {
		tenantId,
		projectId
	};
	const repositoryAccessResult = await db.delete(workAppGitHubMcpToolRepositoryAccess).where(and(projectScopedWhere(workAppGitHubMcpToolRepositoryAccess, scopes), eq(workAppGitHubMcpToolRepositoryAccess.toolId, toolId))).returning();
	const accessModeResult = await db.delete(workAppGitHubMcpToolAccessMode).where(and(projectScopedWhere(workAppGitHubMcpToolAccessMode, scopes), eq(workAppGitHubMcpToolAccessMode.toolId, toolId))).returning();
	const slackMcpDeleted = await deleteSlackMcpToolAccessConfig(db)({
		tenantId,
		projectId,
		toolId
	});
	return {
		mcpToolRepositoryAccessDeleted: repositoryAccessResult.length,
		mcpToolAccessModeDeleted: accessModeResult.length > 0,
		slackMcpToolAccessConfigDeleted: slackMcpDeleted
	};
};
/**
* Delete all GitHub access runtime entities for a specific project.
* Called when a project is deleted from the manage database.
*
* Cleans up:
* - workAppGitHubProjectRepositoryAccess entries
* - workAppGitHubProjectAccessMode entry
* - workAppGitHubMcpToolRepositoryAccess entries (for tools in this project)
* - workAppGitHubMcpToolAccessMode entries (for tools in this project)
*
* @param db - Runtime database client
* @returns Function that performs the cascade delete
*/
const cascadeDeleteGitHubAccessByProject = (db) => async (params) => {
	const scopes = params;
	const projectRepoAccessResult = await db.delete(workAppGitHubProjectRepositoryAccess).where(projectScopedWhere(workAppGitHubProjectRepositoryAccess, scopes)).returning();
	const projectAccessModeResult = await db.delete(workAppGitHubProjectAccessMode).where(projectScopedWhere(workAppGitHubProjectAccessMode, scopes)).returning();
	const mcpToolRepoAccessResult = await db.delete(workAppGitHubMcpToolRepositoryAccess).where(projectScopedWhere(workAppGitHubMcpToolRepositoryAccess, scopes)).returning();
	const mcpToolAccessModeResult = await db.delete(workAppGitHubMcpToolAccessMode).where(projectScopedWhere(workAppGitHubMcpToolAccessMode, scopes)).returning();
	return {
		projectRepositoryAccessDeleted: projectRepoAccessResult.length,
		projectAccessModeDeleted: projectAccessModeResult.length > 0,
		mcpToolRepositoryAccessDeleted: mcpToolRepoAccessResult.length,
		mcpToolAccessModesDeleted: mcpToolAccessModeResult.length
	};
};

//#endregion
export { cascadeDeleteByAgent, cascadeDeleteByBranch, cascadeDeleteByContextConfig, cascadeDeleteByProject, cascadeDeleteBySubAgent, cascadeDeleteByTool, cascadeDeleteGitHubAccessByProject };