import { agents, artifactComponents, contextConfigs, credentialReferences, dataComponents, externalAgents, projects, subAgentArtifactComponents, subAgentDataComponents, subAgentRelations, subAgentToolRelations, subAgents, tools } from "../../db/manage/manage-schema.js";
import { projectScopedWhere, tenantScopedWhere } from "./scope-helpers.js";
import { and, count, desc, eq, inArray } from "drizzle-orm";

//#region src/data-access/manage/projects.ts
/**
* List all unique project IDs within a tenant by scanning all resource tables
* @param projectIds - Optional array of project IDs to filter by. If undefined, returns all projects.
*/
const listProjects = (db) => async (params) => {
	if (params.projectIds !== void 0 && params.projectIds.length === 0) return [];
	const tenantScope = { tenantId: params.tenantId };
	const whereClause = params.projectIds ? and(tenantScopedWhere(projects, tenantScope), inArray(projects.id, params.projectIds)) : tenantScopedWhere(projects, tenantScope);
	const projectsFromTable = await db.select({ projectId: projects.id }).from(projects).where(whereClause);
	if (projectsFromTable.length > 0) return projectsFromTable.map((p) => ({ projectId: p.projectId }));
	const projectIdSets = await Promise.all([
		db.selectDistinct({ projectId: subAgents.projectId }).from(subAgents).where(tenantScopedWhere(subAgents, tenantScope)),
		db.selectDistinct({ projectId: agents.projectId }).from(agents).where(tenantScopedWhere(agents, tenantScope)),
		db.selectDistinct({ projectId: tools.projectId }).from(tools).where(tenantScopedWhere(tools, tenantScope)),
		db.selectDistinct({ projectId: contextConfigs.projectId }).from(contextConfigs).where(tenantScopedWhere(contextConfigs, tenantScope)),
		db.selectDistinct({ projectId: externalAgents.projectId }).from(externalAgents).where(tenantScopedWhere(externalAgents, tenantScope)),
		db.selectDistinct({ projectId: subAgentRelations.projectId }).from(subAgentRelations).where(tenantScopedWhere(subAgentRelations, tenantScope)),
		db.selectDistinct({ projectId: subAgentToolRelations.projectId }).from(subAgentToolRelations).where(tenantScopedWhere(subAgentToolRelations, tenantScope)),
		db.selectDistinct({ projectId: subAgentDataComponents.projectId }).from(subAgentDataComponents).where(tenantScopedWhere(subAgentDataComponents, tenantScope)),
		db.selectDistinct({ projectId: subAgentArtifactComponents.projectId }).from(subAgentArtifactComponents).where(tenantScopedWhere(subAgentArtifactComponents, tenantScope)),
		db.selectDistinct({ projectId: dataComponents.projectId }).from(dataComponents).where(tenantScopedWhere(dataComponents, tenantScope)),
		db.selectDistinct({ projectId: artifactComponents.projectId }).from(artifactComponents).where(tenantScopedWhere(artifactComponents, tenantScope)),
		db.selectDistinct({ projectId: credentialReferences.projectId }).from(credentialReferences).where(tenantScopedWhere(credentialReferences, tenantScope))
	]);
	const allProjectIds = /* @__PURE__ */ new Set();
	projectIdSets.forEach((results) => {
		results.forEach((row) => {
			if (row.projectId) {
				if (!params.projectIds || params.projectIds.includes(row.projectId)) allProjectIds.add(row.projectId);
			}
		});
	});
	return Array.from(allProjectIds).sort().map((projectId) => ({ projectId }));
};
/**
* List all unique project IDs within a tenant with pagination
* Optionally filter by a list of project IDs (for access control)
*/
const listProjectsPaginated = (db) => async (params) => {
	const page = params.pagination?.page || 1;
	const limit = params.pagination?.limit || 10;
	const offset = (page - 1) * limit;
	const tenantScope = { tenantId: params.tenantId };
	const whereClause = params.projectIds ? and(tenantScopedWhere(projects, tenantScope), inArray(projects.id, params.projectIds)) : tenantScopedWhere(projects, tenantScope);
	const [data, totalResult] = await Promise.all([db.select().from(projects).where(whereClause).limit(limit).offset(offset).orderBy(desc(projects.createdAt)), db.select({ count: count() }).from(projects).where(whereClause)]);
	const total = totalResult[0]?.count || 0;
	return {
		data,
		pagination: {
			page,
			limit,
			total,
			pages: Math.ceil(total / limit)
		}
	};
};
/**
* Get resource counts for a specific project
*/
const getProjectResourceCounts = (db) => async (params) => {
	const [subAgentResults, agentResults, toolResults, contextConfigResults, externalAgentResults] = await Promise.all([
		db.select({ count: subAgents.id }).from(subAgents).where(projectScopedWhere(subAgents, params)),
		db.select({ count: agents.id }).from(agents).where(projectScopedWhere(agents, params)),
		db.select({ count: tools.id }).from(tools).where(projectScopedWhere(tools, params)),
		db.select({ count: contextConfigs.id }).from(contextConfigs).where(projectScopedWhere(contextConfigs, params)),
		db.select({ count: externalAgents.id }).from(externalAgents).where(projectScopedWhere(externalAgents, params))
	]);
	return {
		subAgents: subAgentResults.length,
		agents: agentResults.length,
		tools: toolResults.length,
		contextConfigs: contextConfigResults.length,
		externalAgents: externalAgentResults.length
	};
};
/**
* Check if a project exists (has any resources)
*/
const projectExists = (db) => async (params) => {
	const checks = [
		db.select({ id: subAgents.id }).from(subAgents).where(projectScopedWhere(subAgents, params)).limit(1),
		db.select({ id: agents.id }).from(agents).where(projectScopedWhere(agents, params)).limit(1),
		db.select({ id: tools.id }).from(tools).where(projectScopedWhere(tools, params)).limit(1),
		db.select({ id: contextConfigs.id }).from(contextConfigs).where(projectScopedWhere(contextConfigs, params)).limit(1),
		db.select({ id: externalAgents.id }).from(externalAgents).where(projectScopedWhere(externalAgents, params)).limit(1)
	];
	return (await Promise.all(checks)).some((result) => result.length > 0);
};
/**
* Count total projects for a tenant
*/
const countProjects = (db) => async (params) => {
	return (await listProjects(db)(params)).length;
};
/**
* Get a single project by ID
*/
const getProject = (db) => async (params) => {
	return await db.query.projects.findFirst({ where: and(tenantScopedWhere(projects, params.scopes), eq(projects.id, params.scopes.projectId)) }) || null;
};
/**
* Create a new project
*/
const createProject = (db) => async (params) => {
	const now = (/* @__PURE__ */ new Date()).toISOString();
	const [created] = await db.insert(projects).values({
		...params,
		createdAt: now,
		updatedAt: now
	}).returning();
	return created;
};
/**
* Update an existing project
*/
const updateProject = (db) => async (params) => {
	const now = (/* @__PURE__ */ new Date()).toISOString();
	const projectWhere = and(tenantScopedWhere(projects, params.scopes), eq(projects.id, params.scopes.projectId));
	const currentProject = await db.query.projects.findFirst({ where: projectWhere });
	const [updated] = await db.update(projects).set({
		...params.data,
		updatedAt: now
	}).where(projectWhere).returning();
	if (updated && params.data.stopWhen !== void 0) try {
		await cascadeStopWhenUpdates(db, params.scopes, currentProject?.stopWhen, params.data.stopWhen);
	} catch (error) {
		console.warn("Failed to cascade stopWhen updates:", error);
	}
	return updated || null;
};
/**
* Check if a project exists in the projects table
*/
const projectExistsInTable = (db) => async (params) => {
	return (await db.select({ id: projects.id }).from(projects).where(and(tenantScopedWhere(projects, params.scopes), eq(projects.id, params.scopes.projectId))).limit(1)).length > 0;
};
/**
* Check if a project has any resources (used before deletion)
*/
const projectHasResources = (db) => async (params) => {
	return await projectExists(db)(params);
};
/**
* Delete a project (with validation for existing resources)
*/
const deleteProject = (db) => async (params) => {
	if (!await projectExistsInTable(db)({ scopes: params.scopes })) return false;
	await db.delete(projects).where(and(tenantScopedWhere(projects, params.scopes), eq(projects.id, params.scopes.projectId)));
	return true;
};
/**
* Cascade stopWhen updates from project to Agents and Sub Agents
*/
async function cascadeStopWhenUpdates(db, scopes, oldStopWhen, newStopWhen) {
	if (oldStopWhen?.transferCountIs !== newStopWhen?.transferCountIs) {
		const agentsToUpdate = await db.query.agents.findMany({ where: projectScopedWhere(agents, scopes) });
		for (const agent of agentsToUpdate) {
			const agentStopWhen = agent.stopWhen;
			if (!agentStopWhen?.transferCountIs || agentStopWhen.transferCountIs === oldStopWhen?.transferCountIs) {
				const updatedStopWhen = {
					...agentStopWhen || {},
					transferCountIs: newStopWhen?.transferCountIs
				};
				await db.update(agents).set({
					stopWhen: updatedStopWhen,
					updatedAt: (/* @__PURE__ */ new Date()).toISOString()
				}).where(and(projectScopedWhere(agents, scopes), eq(agents.id, agent.id)));
			}
		}
	}
	if (oldStopWhen?.stepCountIs !== newStopWhen?.stepCountIs) {
		const agentsToUpdate = await db.query.subAgents.findMany({ where: projectScopedWhere(subAgents, scopes) });
		for (const agent of agentsToUpdate) {
			const agentStopWhen = agent.stopWhen;
			if (!agentStopWhen?.stepCountIs || agentStopWhen.stepCountIs === oldStopWhen?.stepCountIs) {
				const updatedStopWhen = {
					...agentStopWhen || {},
					stepCountIs: newStopWhen?.stepCountIs
				};
				await db.update(subAgents).set({
					stopWhen: updatedStopWhen,
					updatedAt: (/* @__PURE__ */ new Date()).toISOString()
				}).where(and(projectScopedWhere(subAgents, scopes), eq(subAgents.id, agent.id)));
			}
		}
	}
}

//#endregion
export { countProjects, createProject, deleteProject, getProject, getProjectResourceCounts, listProjects, listProjectsPaginated, projectExists, projectExistsInTable, projectHasResources, updateProject };