import { dataComponents, subAgentDataComponents } from "../../db/manage/manage-schema.js";
import { validatePropsAsJsonSchema } from "../../validation/props-validation.js";
import { validateRender } from "../../validation/render-validation.js";
import { createApiError } from "../../utils/error.js";
import { generateId } from "../../utils/conversations.js";
import { projectScopedWhere, subAgentScopedWhere } from "./scope-helpers.js";
import "../../utils/index.js";
import { and, count, desc, eq } from "drizzle-orm";

//#region src/data-access/manage/dataComponents.ts
/**
* Get a data component by ID
*/
const getDataComponent = (db) => async (params) => {
	return await db.query.dataComponents.findFirst({ where: and(projectScopedWhere(dataComponents, params.scopes), eq(dataComponents.id, params.dataComponentId)) }) || null;
};
/**
* List all data components for a tenant/project
*/
const listDataComponents = (db) => async (params) => {
	return await db.select().from(dataComponents).where(projectScopedWhere(dataComponents, params.scopes)).orderBy(desc(dataComponents.createdAt));
};
/**
* List data components with pagination
*/
const listDataComponentsPaginated = (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 = projectScopedWhere(dataComponents, params.scopes);
	const [data, totalResult] = await Promise.all([db.select().from(dataComponents).where(whereClause).limit(limit).offset(offset).orderBy(desc(dataComponents.createdAt)), db.select({ count: count() }).from(dataComponents).where(whereClause)]);
	const total = typeof totalResult[0]?.count === "string" ? parseInt(totalResult[0].count, 10) : totalResult[0]?.count || 0;
	return {
		data,
		pagination: {
			page,
			limit,
			total,
			pages: Math.ceil(total / limit)
		}
	};
};
/**
* Create a new data component
*/
const createDataComponent = (db) => async (params) => {
	if (params.props) {
		const propsValidation = validatePropsAsJsonSchema(params.props);
		if (!propsValidation.isValid) {
			const errorMessages = propsValidation.errors.map((e) => `${e.field}: ${e.message}`).join(", ");
			throw new Error(`Invalid props schema: ${errorMessages}`);
		}
	}
	if (params.render !== void 0 && params.render !== null) {
		if (typeof params.render === "object" && params.render !== null && "component" in params.render && "mockData" in params.render) {
			const renderValidation = validateRender(params.render);
			if (!renderValidation.isValid) {
				const errorMessages = renderValidation.errors.map((e) => `${e.field}: ${e.message}`).join(", ");
				throw new Error(`Invalid render: ${errorMessages}`);
			}
		}
	}
	return (await db.insert(dataComponents).values(params).returning())[0];
};
/**
* Update a data component
*/
const updateDataComponent = (db) => async (params) => {
	if (params.data.props !== void 0 && params.data.props !== null) {
		const propsValidation = validatePropsAsJsonSchema(params.data.props);
		if (!propsValidation.isValid) throw createApiError({
			code: "bad_request",
			message: `Invalid props schema: ${propsValidation.errors.map((e) => `${e.field}: ${e.message}`).join(", ")}`
		});
	}
	if (params.data.render) {
		if (typeof params.data.render === "object" && "component" in params.data.render && "mockData" in params.data.render) {
			const renderValidation = validateRender(params.data.render);
			if (!renderValidation.isValid) throw createApiError({
				code: "bad_request",
				message: `Invalid render: ${renderValidation.errors.map((e) => `${e.field}: ${e.message}`).join(", ")}`
			});
		}
	}
	const now = (/* @__PURE__ */ new Date()).toISOString();
	await db.update(dataComponents).set({
		...params.data,
		updatedAt: now
	}).where(and(projectScopedWhere(dataComponents, params.scopes), eq(dataComponents.id, params.dataComponentId)));
	return await getDataComponent(db)({
		scopes: params.scopes,
		dataComponentId: params.dataComponentId
	});
};
/**
* Delete a data component
*/
const deleteDataComponent = (db) => async (params) => {
	return (await db.delete(dataComponents).where(and(projectScopedWhere(dataComponents, params.scopes), eq(dataComponents.id, params.dataComponentId))).returning()).length > 0;
};
/**
* Get data components for a specific agent
*/
const getDataComponentsForAgent = (db) => async (params) => {
	return await db.select({
		id: dataComponents.id,
		tenantId: dataComponents.tenantId,
		projectId: dataComponents.projectId,
		name: dataComponents.name,
		description: dataComponents.description,
		props: dataComponents.props,
		createdAt: dataComponents.createdAt,
		updatedAt: dataComponents.updatedAt,
		render: dataComponents.render
	}).from(dataComponents).innerJoin(subAgentDataComponents, and(eq(dataComponents.id, subAgentDataComponents.dataComponentId), eq(dataComponents.tenantId, subAgentDataComponents.tenantId), eq(dataComponents.projectId, subAgentDataComponents.projectId))).where(and(projectScopedWhere(dataComponents, params.scopes), eq(subAgentDataComponents.agentId, params.scopes.agentId), eq(subAgentDataComponents.subAgentId, params.scopes.subAgentId))).orderBy(desc(dataComponents.createdAt));
};
/**
* Associate a data component with an agent
*/
const associateDataComponentWithAgent = (db) => async (params) => {
	return (await db.insert(subAgentDataComponents).values({
		id: generateId(),
		tenantId: params.scopes.tenantId,
		projectId: params.scopes.projectId,
		agentId: params.scopes.agentId,
		subAgentId: params.scopes.subAgentId,
		dataComponentId: params.dataComponentId
	}).returning())[0];
};
/**
* Remove association between data component and agent
*/
const removeDataComponentFromAgent = (db) => async (params) => {
	return (await db.delete(subAgentDataComponents).where(and(subAgentScopedWhere(subAgentDataComponents, params.scopes), eq(subAgentDataComponents.dataComponentId, params.dataComponentId))).returning()).length > 0;
};
const deleteAgentDataComponentRelationByAgent = (db) => async (params) => {
	return (await db.delete(subAgentDataComponents).where(subAgentScopedWhere(subAgentDataComponents, params.scopes)).returning()).length > 0;
};
/**
* Get all agents that use a specific data component
*/
const getAgentsUsingDataComponent = (db) => async (params) => {
	return await db.select({
		subAgentId: subAgentDataComponents.subAgentId,
		createdAt: subAgentDataComponents.createdAt
	}).from(subAgentDataComponents).where(and(projectScopedWhere(subAgentDataComponents, params.scopes), eq(subAgentDataComponents.dataComponentId, params.dataComponentId))).orderBy(desc(subAgentDataComponents.createdAt));
};
/**
* Check if a data component is associated with an agent
*/
const isDataComponentAssociatedWithAgent = (db) => async (params) => {
	return (await db.select({ id: subAgentDataComponents.id }).from(subAgentDataComponents).where(and(subAgentScopedWhere(subAgentDataComponents, params.scopes), eq(subAgentDataComponents.dataComponentId, params.dataComponentId))).limit(1)).length > 0;
};
/**
* Upsert agent-data component relation (create if it doesn't exist, no-op if it does)
*/
const upsertAgentDataComponentRelation = (db) => async (params) => {
	if (!await isDataComponentAssociatedWithAgent(db)(params)) return await associateDataComponentWithAgent(db)(params);
	return null;
};
/**
* Count data components for a tenant/project
*/
const countDataComponents = (db) => async (params) => {
	const countValue = (await db.select({ count: count() }).from(dataComponents).where(projectScopedWhere(dataComponents, params.scopes)))[0]?.count;
	return typeof countValue === "string" ? parseInt(countValue, 10) : countValue || 0;
};
/**
* Upsert a data component (create if it doesn't exist, update if it does)
*/
const upsertDataComponent = (db) => async (params) => {
	const scopes = {
		tenantId: params.data.tenantId,
		projectId: params.data.projectId
	};
	if (await getDataComponent(db)({
		scopes,
		dataComponentId: params.data.id
	})) return await updateDataComponent(db)({
		scopes,
		dataComponentId: params.data.id,
		data: {
			name: params.data.name,
			description: params.data.description,
			props: params.data.props
		}
	});
	return await createDataComponent(db)(params.data);
};

//#endregion
export { associateDataComponentWithAgent, countDataComponents, createDataComponent, deleteAgentDataComponentRelationByAgent, deleteDataComponent, getAgentsUsingDataComponent, getDataComponent, getDataComponentsForAgent, isDataComponentAssociatedWithAgent, listDataComponents, listDataComponentsPaginated, removeDataComponentFromAgent, updateDataComponent, upsertAgentDataComponentRelation, upsertDataComponent };