import { credentialReferences, externalAgents, tools } from "../../db/manage/manage-schema.js";
import { isUniqueConstraintError } from "../../utils/error.js";
import { projectScopedWhere } from "./scope-helpers.js";
import { and, count, desc, eq, sql } from "drizzle-orm";

//#region src/data-access/manage/credentialReferences.ts
/**
* Get a credential reference by ID
*/
const getCredentialReference = (db) => async (params) => {
	return await db.query.credentialReferences.findFirst({ where: and(projectScopedWhere(credentialReferences, params.scopes), eq(credentialReferences.id, params.id)) });
};
/**
* Get a user-scoped credential reference by toolId and userId
*/
const getUserScopedCredentialReference = (db) => async (params) => {
	return await db.query.credentialReferences.findFirst({ where: and(projectScopedWhere(credentialReferences, params.scopes), eq(credentialReferences.toolId, params.toolId), eq(credentialReferences.userId, params.userId)) });
};
/**
* Get a credential reference by ID with its related tools
*/
const getCredentialReferenceWithResources = (db) => async (params) => {
	const [credential, relatedTools, relatedExternalAgents] = await Promise.all([
		db.query.credentialReferences.findFirst({ where: and(projectScopedWhere(credentialReferences, params.scopes), eq(credentialReferences.id, params.id)) }),
		db.select().from(tools).where(and(projectScopedWhere(tools, params.scopes), eq(tools.credentialReferenceId, params.id))),
		db.select().from(externalAgents).where(and(projectScopedWhere(externalAgents, params.scopes), eq(externalAgents.credentialReferenceId, params.id)))
	]);
	if (!credential) return;
	return {
		...credential,
		tools: relatedTools,
		externalAgents: relatedExternalAgents
	};
};
/**
* List all credential references for a tenant/project
*/
const listCredentialReferences = (db) => async (params) => {
	return await db.query.credentialReferences.findMany({
		where: projectScopedWhere(credentialReferences, params.scopes),
		orderBy: [desc(credentialReferences.createdAt)]
	});
};
/**
* List credential references with pagination
*/
const listCredentialReferencesPaginated = (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(credentialReferences, params.scopes);
	const [data, totalResult] = await Promise.all([db.select().from(credentialReferences).where(whereClause).limit(limit).offset(offset).orderBy(desc(credentialReferences.createdAt)), db.select({ count: sql`COUNT(*)` }).from(credentialReferences).where(whereClause)]);
	const total = Number(totalResult[0]?.count || 0);
	return {
		data,
		pagination: {
			page,
			limit,
			total,
			pages: Math.ceil(total / limit)
		}
	};
};
/**
* Create a new credential reference
*/
const createCredentialReference = (db) => async (params) => {
	const now = (/* @__PURE__ */ new Date()).toISOString();
	const [credentialReference] = await db.insert(credentialReferences).values({
		...params,
		createdAt: now,
		updatedAt: now
	}).returning();
	return credentialReference;
};
/**
* Update a credential reference
*/
const updateCredentialReference = (db) => async (params) => {
	const now = (/* @__PURE__ */ new Date()).toISOString();
	await db.update(credentialReferences).set({
		...params.data,
		updatedAt: now
	}).where(and(projectScopedWhere(credentialReferences, params.scopes), eq(credentialReferences.id, params.id)));
	return await getCredentialReferenceWithResources(db)({
		scopes: params.scopes,
		id: params.id
	});
};
/**
* Delete a credential reference
*/
const deleteCredentialReference = (db) => async (params) => {
	if (!await getCredentialReference(db)({
		scopes: params.scopes,
		id: params.id
	})) return false;
	await db.delete(credentialReferences).where(and(projectScopedWhere(credentialReferences, params.scopes), eq(credentialReferences.id, params.id)));
	return await getCredentialReference(db)({
		scopes: params.scopes,
		id: params.id
	}) === void 0;
};
/**
* Check if a credential reference exists
*/
const hasCredentialReference = (db) => async (params) => {
	return await getCredentialReference(db)(params) !== null;
};
/**
* Get credential reference by ID (simple version without tools)
*/
const getCredentialReferenceById = (db) => async (params) => {
	return await db.query.credentialReferences.findFirst({ where: and(projectScopedWhere(credentialReferences, params.scopes), eq(credentialReferences.id, params.id)) }) || null;
};
/**
* Count credential references for a tenant/project
*/
const countCredentialReferences = (db) => async (params) => {
	const total = (await db.select({ count: count() }).from(credentialReferences).where(projectScopedWhere(credentialReferences, params.scopes)))[0]?.count || 0;
	return typeof total === "string" ? Number.parseInt(total, 10) : total;
};
/**
* Upsert a credential reference (create if it doesn't exist, update if it does).
* For user-scoped credentials (toolId + userId set), also checks the unique constraint pair.
* Uses application-level find-then-update since Doltgres does not support ON CONFLICT DO UPDATE.
*/
const upsertCredentialReference = (db) => async (params) => {
	const scopes = {
		tenantId: params.data.tenantId,
		projectId: params.data.projectId
	};
	const existingById = await getCredentialReference(db)({
		scopes,
		id: params.data.id
	});
	if (existingById) {
		const updated = await updateCredentialReference(db)({
			scopes,
			id: existingById.id,
			data: {
				name: params.data.name,
				type: params.data.type,
				credentialStoreId: params.data.credentialStoreId,
				retrievalParams: params.data.retrievalParams
			}
		});
		if (!updated) throw new Error("Failed to update credential reference - no rows affected");
		return updated;
	}
	if (params.data.toolId && params.data.userId) {
		const existingByToolUser = await getUserScopedCredentialReference(db)({
			scopes,
			toolId: params.data.toolId,
			userId: params.data.userId
		});
		if (existingByToolUser) {
			const updated = await updateCredentialReference(db)({
				scopes,
				id: existingByToolUser.id,
				data: {
					name: params.data.name,
					type: params.data.type,
					credentialStoreId: params.data.credentialStoreId,
					retrievalParams: params.data.retrievalParams
				}
			});
			if (!updated) throw new Error("Failed to update credential reference - no rows affected");
			return updated;
		}
	}
	try {
		return await createCredentialReference(db)(params.data);
	} catch (error) {
		if (isUniqueConstraintError(error) && params.data.toolId && params.data.userId) {
			const raceWinner = await getUserScopedCredentialReference(db)({
				scopes,
				toolId: params.data.toolId,
				userId: params.data.userId
			});
			if (raceWinner) {
				const updated = await updateCredentialReference(db)({
					scopes,
					id: raceWinner.id,
					data: {
						name: params.data.name,
						type: params.data.type,
						credentialStoreId: params.data.credentialStoreId,
						retrievalParams: params.data.retrievalParams
					}
				});
				if (updated) return updated;
			}
		}
		throw error;
	}
};

//#endregion
export { countCredentialReferences, createCredentialReference, deleteCredentialReference, getCredentialReference, getCredentialReferenceById, getCredentialReferenceWithResources, getUserScopedCredentialReference, hasCredentialReference, listCredentialReferences, listCredentialReferencesPaginated, updateCredentialReference, upsertCredentialReference };