import { conversations, datasetRun, datasetRunConversationRelations, evaluationResult, evaluationRun, messages } from "../../db/runtime/runtime-schema.js";
import { projectScopedWhere } from "../manage/scope-helpers.js";
import { and, asc, count, desc, eq, gte, inArray, lte, sql } from "drizzle-orm";

//#region src/data-access/runtime/evalRuns.ts
const getDatasetRunById = (db) => async (params) => {
	return (await db.select().from(datasetRun).where(and(projectScopedWhere(datasetRun, params.scopes), eq(datasetRun.id, params.scopes.datasetRunId))).limit(1))[0] ?? null;
};
const listDatasetRuns = (db) => async (params) => {
	return await db.select().from(datasetRun).where(projectScopedWhere(datasetRun, params.scopes)).orderBy(desc(datasetRun.createdAt));
};
const getDatasetRunsByIds = (db) => async (params) => {
	if (params.datasetRunIds.length === 0) return [];
	return await db.select().from(datasetRun).where(and(projectScopedWhere(datasetRun, params.scopes), inArray(datasetRun.id, params.datasetRunIds)));
};
const listDatasetRunsByConfig = (db) => async (params) => {
	return await db.select().from(datasetRun).where(and(projectScopedWhere(datasetRun, params.scopes), eq(datasetRun.datasetRunConfigId, params.scopes.datasetRunConfigId))).orderBy(desc(datasetRun.createdAt));
};
const createDatasetRun = (db) => async (data) => {
	const now = (/* @__PURE__ */ new Date()).toISOString();
	const [created] = await db.insert(datasetRun).values({
		...data,
		createdAt: now,
		updatedAt: now
	}).returning();
	return created;
};
const deleteDatasetRun = (db) => async (params) => {
	return (await db.delete(datasetRun).where(and(projectScopedWhere(datasetRun, params.scopes), eq(datasetRun.id, params.scopes.datasetRunId))).returning()).length > 0;
};
const getDatasetRunConversationRelations = (db) => async (params) => {
	return await db.select().from(datasetRunConversationRelations).where(and(projectScopedWhere(datasetRunConversationRelations, params.scopes), eq(datasetRunConversationRelations.datasetRunId, params.scopes.datasetRunId)));
};
const createDatasetRunConversationRelation = (db) => async (data) => {
	const now = (/* @__PURE__ */ new Date()).toISOString();
	const [created] = await db.insert(datasetRunConversationRelations).values({
		...data,
		createdAt: now,
		updatedAt: now
	}).returning();
	return created;
};
const createDatasetRunConversationRelations = (db) => async (data) => {
	const now = (/* @__PURE__ */ new Date()).toISOString();
	const values = data.map((item) => ({
		...item,
		createdAt: now,
		updatedAt: now
	}));
	return await db.insert(datasetRunConversationRelations).values(values).returning();
};
const deleteDatasetRunConversationRelation = (db) => async (params) => {
	return (await db.delete(datasetRunConversationRelations).where(and(projectScopedWhere(datasetRunConversationRelations, params.scopes), eq(datasetRunConversationRelations.datasetRunId, params.scopes.datasetRunId), eq(datasetRunConversationRelations.conversationId, params.scopes.conversationId))).returning()).length > 0;
};
const deleteDatasetRunConversationRelationsByRun = (db) => async (params) => {
	return (await db.delete(datasetRunConversationRelations).where(and(projectScopedWhere(datasetRunConversationRelations, params.scopes), eq(datasetRunConversationRelations.datasetRunId, params.scopes.datasetRunId))).returning()).length;
};
const getDatasetRunConversationRelationByConversation = (db) => async (params) => {
	return (await db.select().from(datasetRunConversationRelations).where(and(projectScopedWhere(datasetRunConversationRelations, params.scopes), eq(datasetRunConversationRelations.conversationId, params.scopes.conversationId))).limit(1))[0] || null;
};
const getEvaluationRunById = (db) => async (params) => {
	return (await db.select().from(evaluationRun).where(and(projectScopedWhere(evaluationRun, params.scopes), eq(evaluationRun.id, params.scopes.evaluationRunId))).limit(1))[0] ?? null;
};
const listEvaluationRuns = (db) => async (params) => {
	return await db.select().from(evaluationRun).where(projectScopedWhere(evaluationRun, params.scopes));
};
const listEvaluationRunsByJobConfigId = (db) => async (params) => {
	return await db.select().from(evaluationRun).where(and(projectScopedWhere(evaluationRun, params.scopes), eq(evaluationRun.evaluationJobConfigId, params.evaluationJobConfigId)));
};
const getEvaluationRunByJobConfigId = (db) => async (params) => {
	return (await db.select().from(evaluationRun).where(and(projectScopedWhere(evaluationRun, params.scopes), eq(evaluationRun.evaluationJobConfigId, params.evaluationJobConfigId))).limit(1))[0] ?? null;
};
const createEvaluationRun = (db) => async (data) => {
	const now = (/* @__PURE__ */ new Date()).toISOString();
	const [created] = await db.insert(evaluationRun).values({
		...data,
		createdAt: now,
		updatedAt: now
	}).returning();
	return created;
};
const updateEvaluationRun = (db) => async (params) => {
	const updateData = { updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
	for (const [key, value] of Object.entries(params.data)) if (value !== void 0) updateData[key] = value;
	const [updated] = await db.update(evaluationRun).set(updateData).where(and(projectScopedWhere(evaluationRun, params.scopes), eq(evaluationRun.id, params.scopes.evaluationRunId))).returning();
	return updated ?? null;
};
const deleteEvaluationRun = (db) => async (params) => {
	return (await db.delete(evaluationRun).where(and(projectScopedWhere(evaluationRun, params.scopes), eq(evaluationRun.id, params.scopes.evaluationRunId))).returning()).length > 0;
};
const getEvaluationResultById = (db) => async (params) => {
	return await db.query.evaluationResult.findFirst({ where: and(projectScopedWhere(evaluationResult, params.scopes), eq(evaluationResult.id, params.scopes.evaluationResultId)) }) ?? null;
};
const listEvaluationResults = (db) => async (params) => {
	return await db.query.evaluationResult.findMany({ where: projectScopedWhere(evaluationResult, params.scopes) });
};
const listEvaluationResultsByRun = (db) => async (params) => {
	return await db.query.evaluationResult.findMany({ where: and(projectScopedWhere(evaluationResult, params.scopes), eq(evaluationResult.evaluationRunId, params.scopes.evaluationRunId)) });
};
const EVAL_RESULTS_MAX_ROWS = 2e4;
function buildFirstUserMessageSubquery(db) {
	return db.selectDistinctOn([messages.conversationId], {
		conversationId: messages.conversationId,
		content: messages.content
	}).from(messages).where(eq(messages.role, "user")).orderBy(messages.conversationId, asc(messages.createdAt), asc(messages.id)).as("first_msg");
}
function buildEvalResultsBaseQuery(db, params) {
	const runWhereConditions = [projectScopedWhere(evaluationRun, params.scopes)];
	if (params.evaluationRunConfigId) runWhereConditions.push(eq(evaluationRun.evaluationRunConfigId, params.evaluationRunConfigId));
	if (params.evaluationJobConfigId) runWhereConditions.push(eq(evaluationRun.evaluationJobConfigId, params.evaluationJobConfigId));
	const runIdSubquery = db.select({ id: evaluationRun.id }).from(evaluationRun).where(and(...runWhereConditions));
	const whereConditions = [projectScopedWhere(evaluationResult, params.scopes), inArray(evaluationResult.evaluationRunId, runIdSubquery)];
	if (params.filters?.evaluatorId) whereConditions.push(eq(evaluationResult.evaluatorId, params.filters.evaluatorId));
	if (params.filters?.agentId) whereConditions.push(eq(conversations.agentId, params.filters.agentId));
	if (params.filters?.conversationId) whereConditions.push(eq(evaluationResult.conversationId, params.filters.conversationId));
	if (params.filters?.startDate) whereConditions.push(gte(conversations.createdAt, params.filters.startDate));
	if (params.filters?.endDate) whereConditions.push(lte(conversations.createdAt, params.filters.endDate));
	return { whereConditions };
}
const listEvaluationResultsPaginated = (db) => async (params) => {
	const page = params.pagination?.page ?? 1;
	const limit = Math.min(params.pagination?.limit ?? 50, EVAL_RESULTS_MAX_ROWS);
	const offset = (page - 1) * limit;
	const { whereConditions } = buildEvalResultsBaseQuery(db, {
		scopes: params.scopes,
		evaluationRunConfigId: params.evaluationRunConfigId,
		evaluationJobConfigId: params.evaluationJobConfigId,
		filters: params.filters
	});
	const conversationsJoin = and(eq(evaluationResult.conversationId, conversations.id), eq(evaluationResult.tenantId, conversations.tenantId), eq(evaluationResult.projectId, conversations.projectId));
	const firstMsg = buildFirstUserMessageSubquery(db);
	const [countResult, rows, agentIdRows, outputKeyRows] = await Promise.all([
		db.select({
			total: count(),
			completedCount: count(evaluationResult.output)
		}).from(evaluationResult).leftJoin(conversations, conversationsJoin).where(and(...whereConditions)),
		db.select({
			id: evaluationResult.id,
			tenantId: evaluationResult.tenantId,
			projectId: evaluationResult.projectId,
			conversationId: evaluationResult.conversationId,
			evaluatorId: evaluationResult.evaluatorId,
			evaluationRunId: evaluationResult.evaluationRunId,
			output: evaluationResult.output,
			createdAt: evaluationResult.createdAt,
			updatedAt: evaluationResult.updatedAt,
			agentId: conversations.agentId,
			conversationCreatedAt: conversations.createdAt,
			firstMsgContent: firstMsg.content
		}).from(evaluationResult).leftJoin(conversations, conversationsJoin).leftJoin(firstMsg, eq(evaluationResult.conversationId, firstMsg.conversationId)).where(and(...whereConditions)).orderBy(desc(evaluationResult.createdAt)).limit(limit).offset(offset),
		db.selectDistinct({ agentId: conversations.agentId }).from(evaluationResult).leftJoin(conversations, conversationsJoin).where(and(...whereConditions)),
		db.select({ key: sql`jsonb_object_keys(${evaluationResult.output}->'output')` }).from(evaluationResult).leftJoin(conversations, conversationsJoin).where(and(...whereConditions, sql`jsonb_typeof(${evaluationResult.output}->'output') = 'object'`)).groupBy(sql`jsonb_object_keys(${evaluationResult.output}->'output')`)
	]);
	const total = countResult[0]?.total ?? 0;
	const completedCount = countResult[0]?.completedCount ?? 0;
	const distinctAgentIds = agentIdRows.map((r) => r.agentId).filter((id) => id != null).sort();
	const distinctOutputKeys = outputKeyRows.map((r) => `output.${r.key}`).sort();
	return {
		data: rows.map((row) => {
			let input = null;
			if (row.firstMsgContent) {
				const content = row.firstMsgContent;
				if (content.text) input = content.text;
				else if (content.parts) input = content.parts.filter((part) => part.kind === "text" && part.text).map((part) => part.text).join(" ") || null;
			}
			return {
				id: row.id,
				tenantId: row.tenantId,
				projectId: row.projectId,
				conversationId: row.conversationId,
				evaluatorId: row.evaluatorId,
				evaluationRunId: row.evaluationRunId,
				output: row.output,
				createdAt: row.createdAt,
				updatedAt: row.updatedAt,
				input,
				agentId: row.agentId ?? null,
				conversationCreatedAt: row.conversationCreatedAt ?? null
			};
		}),
		pagination: {
			page,
			limit,
			total,
			pages: Math.ceil(total / limit),
			completedCount
		},
		distinctAgentIds,
		distinctOutputKeys
	};
};
const listEvaluationResultsByConversation = (db) => async (params) => {
	return await db.query.evaluationResult.findMany({ where: and(projectScopedWhere(evaluationResult, params.scopes), eq(evaluationResult.conversationId, params.scopes.conversationId)) });
};
const createEvaluationResult = (db) => async (data) => {
	const now = (/* @__PURE__ */ new Date()).toISOString();
	const [created] = await db.insert(evaluationResult).values({
		...data,
		createdAt: now,
		updatedAt: now
	}).returning();
	return created;
};
const createEvaluationResults = (db) => async (data) => {
	const now = (/* @__PURE__ */ new Date()).toISOString();
	const values = data.map((item) => ({
		...item,
		createdAt: now,
		updatedAt: now
	}));
	return await db.insert(evaluationResult).values(values).returning();
};
const updateEvaluationResult = (db) => async (params) => {
	const updateData = { updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
	for (const [key, value] of Object.entries(params.data)) if (value !== void 0) updateData[key] = value;
	const [updated] = await db.update(evaluationResult).set(updateData).where(and(projectScopedWhere(evaluationResult, params.scopes), eq(evaluationResult.id, params.scopes.evaluationResultId))).returning();
	return updated ?? null;
};
const deleteEvaluationResult = (db) => async (params) => {
	return (await db.delete(evaluationResult).where(and(projectScopedWhere(evaluationResult, params.scopes), eq(evaluationResult.id, params.scopes.evaluationResultId))).returning()).length > 0;
};
const deleteEvaluationResultsByRun = (db) => async (params) => {
	return (await db.delete(evaluationResult).where(and(projectScopedWhere(evaluationResult, params.scopes), eq(evaluationResult.evaluationRunId, params.scopes.evaluationRunId))).returning()).length;
};
/**
* Helper to extract plain filter criteria from a Filter wrapper.
* Currently only handles plain objects - and/or combinators are not yet supported.
*/
function extractPlainFilterCriteria(filter) {
	if (!filter) return null;
	if ("and" in filter || "or" in filter) return null;
	return filter;
}
/**
* Filter conversations based on evaluation job filter criteria
*/
const filterConversationsForJob = (db) => async (params) => {
	const { scopes, jobFilters: rawJobFilters } = params;
	const jobFilters = extractPlainFilterCriteria(rawJobFilters);
	const { tenantId, projectId } = scopes;
	const whereConditions = [projectScopedWhere(conversations, scopes)];
	if (jobFilters?.conversationIds && Array.isArray(jobFilters.conversationIds) && jobFilters.conversationIds.length > 0) whereConditions.push(inArray(conversations.id, jobFilters.conversationIds));
	if (jobFilters?.dateRange) {
		const { startDate, endDate } = jobFilters.dateRange;
		if (startDate) whereConditions.push(gte(conversations.createdAt, startDate));
		if (endDate) whereConditions.push(lte(conversations.createdAt, endDate));
	}
	if (jobFilters?.datasetRunIds && Array.isArray(jobFilters.datasetRunIds) && jobFilters.datasetRunIds.length > 0) {
		const allConversationIds = /* @__PURE__ */ new Set();
		for (const datasetRunId of jobFilters.datasetRunIds) {
			const relations$1 = await getDatasetRunConversationRelations(db)({ scopes: {
				tenantId,
				projectId,
				datasetRunId
			} });
			for (const relation of relations$1) allConversationIds.add(relation.conversationId);
		}
		if (allConversationIds.size > 0) whereConditions.push(inArray(conversations.id, Array.from(allConversationIds)));
		else return [];
	}
	return await db.select().from(conversations).where(and(...whereConditions));
};

//#endregion
export { createDatasetRun, createDatasetRunConversationRelation, createDatasetRunConversationRelations, createEvaluationResult, createEvaluationResults, createEvaluationRun, deleteDatasetRun, deleteDatasetRunConversationRelation, deleteDatasetRunConversationRelationsByRun, deleteEvaluationResult, deleteEvaluationResultsByRun, deleteEvaluationRun, filterConversationsForJob, getDatasetRunById, getDatasetRunConversationRelationByConversation, getDatasetRunConversationRelations, getDatasetRunsByIds, getEvaluationResultById, getEvaluationRunById, getEvaluationRunByJobConfigId, listDatasetRuns, listDatasetRunsByConfig, listEvaluationResults, listEvaluationResultsByConversation, listEvaluationResultsByRun, listEvaluationResultsPaginated, listEvaluationRuns, listEvaluationRunsByJobConfigId, updateEvaluationResult, updateEvaluationRun };