import { functionTools, subAgentFunctionToolRelations } from "../../db/manage/manage-schema.js";
import { getLogger } from "../../utils/logger.js";
import { deriveRelationId } from "../../utils/conversations.js";
import { agentScopedWhere } from "./scope-helpers.js";
import { and, count, desc, eq } from "drizzle-orm";

//#region src/data-access/manage/functionTools.ts
const logger = getLogger("functionTools");
/**
* Get a function tool by ID (agent-scoped)
*/
const getFunctionToolById = (db) => async (params) => {
	return (await db.select().from(functionTools).where(and(agentScopedWhere(functionTools, params.scopes), eq(functionTools.id, params.functionToolId))).limit(1))[0] ?? null;
};
/**
* List function tools (agent-scoped)
*/
const listFunctionTools = (db) => async (params) => {
	const page = params.pagination?.page || 1;
	const limit = Math.min(params.pagination?.limit || 10, 100);
	const offset = (page - 1) * limit;
	const whereClause = agentScopedWhere(functionTools, params.scopes);
	const [functionToolsDbResults, totalResult] = await Promise.all([db.select().from(functionTools).where(whereClause).limit(limit).offset(offset).orderBy(desc(functionTools.createdAt)), db.select({ count: count() }).from(functionTools).where(whereClause)]);
	const total = totalResult[0]?.count || 0;
	return {
		data: functionToolsDbResults,
		pagination: {
			page,
			limit,
			total,
			pages: Math.ceil(total / limit)
		}
	};
};
/**
* Create a function tool (agent-scoped)
*/
const createFunctionTool = (db) => async (params) => {
	const { data, scopes } = params;
	const [created] = await db.insert(functionTools).values({
		...scopes,
		id: data.id,
		name: data.name,
		description: data.description,
		functionId: data.functionId,
		createdAt: (/* @__PURE__ */ new Date()).toISOString(),
		updatedAt: (/* @__PURE__ */ new Date()).toISOString()
	}).returning();
	return created;
};
/**
* Update a function tool (agent-scoped)
*/
const updateFunctionTool = (db) => async (params) => {
	const now = (/* @__PURE__ */ new Date()).toISOString();
	const [updated] = await db.update(functionTools).set({
		...params.data,
		updatedAt: now
	}).where(and(agentScopedWhere(functionTools, params.scopes), eq(functionTools.id, params.functionToolId))).returning();
	return updated ?? null;
};
/**
* Delete a function tool (agent-scoped)
*/
const deleteFunctionTool = (db) => async (params) => {
	const [deleted] = await db.delete(functionTools).where(and(agentScopedWhere(functionTools, params.scopes), eq(functionTools.id, params.functionToolId))).returning();
	return !!deleted;
};
/**
* Upsert a function tool (create if it doesn't exist, update if it does)
*/
const upsertFunctionTool = (db) => async (params) => {
	if (await getFunctionToolById(db)({
		scopes: params.scopes,
		functionToolId: params.data.id
	})) return await updateFunctionTool(db)({
		scopes: params.scopes,
		functionToolId: params.data.id,
		data: {
			name: params.data.name,
			description: params.data.description,
			functionId: params.data.functionId
		}
	});
	return await createFunctionTool(db)({
		data: params.data,
		scopes: params.scopes
	});
};
const getFunctionToolsForSubAgent = (db) => {
	return async (params) => {
		const page = params.pagination?.page || 1;
		const limit = Math.min(params.pagination?.limit || 1e3, 1e3);
		const offset = (page - 1) * limit;
		try {
			const whereClause = and(agentScopedWhere(subAgentFunctionToolRelations, params.scopes), eq(subAgentFunctionToolRelations.subAgentId, params.subAgentId));
			const [data, totalResult] = await Promise.all([db.select({
				id: functionTools.id,
				name: functionTools.name,
				description: functionTools.description,
				functionId: functionTools.functionId,
				createdAt: functionTools.createdAt,
				updatedAt: functionTools.updatedAt,
				tenantId: functionTools.tenantId,
				projectId: functionTools.projectId,
				agentId: functionTools.agentId,
				relationshipId: subAgentFunctionToolRelations.id,
				toolPolicies: subAgentFunctionToolRelations.toolPolicies
			}).from(subAgentFunctionToolRelations).innerJoin(functionTools, and(eq(subAgentFunctionToolRelations.functionToolId, functionTools.id), eq(subAgentFunctionToolRelations.tenantId, functionTools.tenantId), eq(subAgentFunctionToolRelations.projectId, functionTools.projectId), eq(subAgentFunctionToolRelations.agentId, functionTools.agentId))).where(whereClause).limit(limit).offset(offset).orderBy(desc(subAgentFunctionToolRelations.createdAt)), db.select({ count: count() }).from(subAgentFunctionToolRelations).where(whereClause)]);
			const total = totalResult[0]?.count || 0;
			return {
				data,
				pagination: {
					page,
					limit,
					total,
					pages: Math.ceil(total / limit)
				}
			};
		} catch (error) {
			logger.error({
				...params.scopes,
				subAgentId: params.subAgentId,
				error
			}, "Failed to get function tools for agent");
			throw error;
		}
	};
};
/**
* Upsert a sub_agent-function tool relation (create if it doesn't exist, update if it does)
*/
const upsertSubAgentFunctionToolRelation = (db) => async (params) => {
	const { scopes, subAgentId, functionToolId, toolPolicies, relationId } = params;
	if (relationId) return await updateSubAgentFunctionToolRelation(db)({
		scopes,
		relationId,
		data: {
			subAgentId,
			functionToolId,
			...toolPolicies !== void 0 ? { toolPolicies } : {}
		}
	});
	try {
		const existingRelations = await db.select().from(subAgentFunctionToolRelations).where(and(agentScopedWhere(subAgentFunctionToolRelations, scopes), eq(subAgentFunctionToolRelations.subAgentId, subAgentId), eq(subAgentFunctionToolRelations.functionToolId, functionToolId))).limit(1);
		if (existingRelations.length > 0) {
			logger.info({
				...scopes,
				subAgentId,
				functionToolId,
				relationId: existingRelations[0].id
			}, "Sub_agent-function tool relation already exists, returning existing relation");
			return { id: existingRelations[0].id };
		}
		return await addFunctionToolToSubAgent(db)(params);
	} catch (error) {
		logger.error({
			...scopes,
			subAgentId,
			functionToolId,
			error
		}, "Failed to upsert sub_agent-function tool relation");
		throw error;
	}
};
/**
* Add a function tool to an agent
*/
const addFunctionToolToSubAgent = (db) => {
	return async (params) => {
		const { scopes, subAgentId, functionToolId, toolPolicies } = params;
		try {
			const relationId = deriveRelationId(scopes.tenantId, scopes.projectId, scopes.agentId, subAgentId, functionToolId);
			const [result] = await db.insert(subAgentFunctionToolRelations).values({
				id: relationId,
				...scopes,
				subAgentId,
				functionToolId,
				...toolPolicies !== void 0 ? { toolPolicies } : {}
			}).returning();
			logger.info({
				...scopes,
				subAgentId,
				functionToolId,
				relationId
			}, "Function tool added to sub_agent");
			return result;
		} catch (error) {
			logger.error({
				...scopes,
				subAgentId,
				functionToolId,
				error
			}, "Failed to add function tool to agent");
			throw error;
		}
	};
};
/**
* Update an agent-function tool relation
*/
const updateSubAgentFunctionToolRelation = (db) => {
	return async (params) => {
		const { scopes, relationId, data } = params;
		try {
			await db.update(subAgentFunctionToolRelations).set({
				subAgentId: data.subAgentId,
				functionToolId: data.functionToolId,
				...data.toolPolicies !== void 0 ? { toolPolicies: data.toolPolicies } : {}
			}).where(and(agentScopedWhere(subAgentFunctionToolRelations, scopes), eq(subAgentFunctionToolRelations.id, relationId)));
			logger.info({
				...scopes,
				relationId,
				data
			}, "SubAgent-function tool relation updated");
			return { id: relationId };
		} catch (error) {
			logger.error({
				...scopes,
				relationId,
				data,
				error
			}, "Failed to update agent-function tool relation");
			throw error;
		}
	};
};
/**
* Get all sub-agents that use a specific function tool
*/
const getSubAgentsUsingFunctionTool = (db) => {
	return async (params) => {
		const { scopes, functionToolId } = params;
		try {
			return await db.select({
				subAgentId: subAgentFunctionToolRelations.subAgentId,
				createdAt: subAgentFunctionToolRelations.createdAt
			}).from(subAgentFunctionToolRelations).where(and(agentScopedWhere(subAgentFunctionToolRelations, scopes), eq(subAgentFunctionToolRelations.functionToolId, functionToolId)));
		} catch (error) {
			logger.error({
				...scopes,
				functionToolId,
				error
			}, "Failed to get sub-agents using function tool");
			throw error;
		}
	};
};
/**
* Remove a function tool from a sub-agent
*/
const removeFunctionToolFromSubAgent = (db) => {
	return async (params) => {
		const { scopes, subAgentId, functionToolId } = params;
		try {
			const removed = (await db.delete(subAgentFunctionToolRelations).where(and(agentScopedWhere(subAgentFunctionToolRelations, scopes), eq(subAgentFunctionToolRelations.subAgentId, subAgentId), eq(subAgentFunctionToolRelations.functionToolId, functionToolId))).returning()).length > 0;
			if (removed) logger.info({
				...scopes,
				subAgentId,
				functionToolId
			}, "Function tool removed from sub-agent");
			return removed;
		} catch (error) {
			logger.error({
				...scopes,
				subAgentId,
				functionToolId,
				error
			}, "Failed to remove function tool from sub-agent");
			throw error;
		}
	};
};
/**
* Check if a function tool is associated with a sub-agent
*/
const isFunctionToolAssociatedWithSubAgent = (db) => {
	return async (params) => {
		const { scopes, subAgentId, functionToolId } = params;
		try {
			return (await db.select({ id: subAgentFunctionToolRelations.id }).from(subAgentFunctionToolRelations).where(and(agentScopedWhere(subAgentFunctionToolRelations, scopes), eq(subAgentFunctionToolRelations.subAgentId, subAgentId), eq(subAgentFunctionToolRelations.functionToolId, functionToolId))).limit(1)).length > 0;
		} catch (error) {
			logger.error({
				...scopes,
				subAgentId,
				functionToolId,
				error
			}, "Failed to check function tool association with sub-agent");
			throw error;
		}
	};
};
/**
* Associate a function tool with a sub-agent (alias for addFunctionToolToSubAgent)
*/
const associateFunctionToolWithSubAgent = (db) => {
	return addFunctionToolToSubAgent(db);
};

//#endregion
export { addFunctionToolToSubAgent, associateFunctionToolWithSubAgent, createFunctionTool, deleteFunctionTool, getFunctionToolById, getFunctionToolsForSubAgent, getSubAgentsUsingFunctionTool, isFunctionToolAssociatedWithSubAgent, listFunctionTools, removeFunctionToolFromSubAgent, updateFunctionTool, updateSubAgentFunctionToolRelation, upsertFunctionTool, upsertSubAgentFunctionToolRelation };