import runDbClient_default from "../../../data/db/runDbClient.js";
import manageDbClient_default from "../../../data/db/manageDbClient.js";
import { requireProjectPermission } from "../../../middleware/projectAccess.js";
import { speakeasyOffsetLimitPagination } from "../../../utils/speakeasy.js";
import { requireEntitlement } from "../../../middleware/requireEntitlement.js";
import { requirePermission } from "../../../middleware/requirePermission.js";
import { OpenAPIHono } from "@hono/zod-openapi";
import { ErrorResponseSchema, PaginationQueryParamsSchema, ProjectApiInsertSchema, ProjectApiUpdateSchema, ProjectListResponse, ProjectResponse, QUOTA_RESOURCE_TYPES, TenantIdParamsSchema, TenantParamsSchema, cascadeDeleteByProject, commonGetErrorResponses, countProjectsInRuntime, createApiError, createProject, createProjectMetadataAndBranch, deleteProject, deleteProjectWithBranch, doltCheckout, getProject, getProjectMainBranchName, listAccessibleProjectIds, listProjectsWithMetadataPaginated, removeProjectFromSpiceDb, syncProjectToSpiceDb, throwIfUniqueConstraintError, updateProject } from "@inkeep/agents-core";
import { createProtectedRoute, inheritedManageTenantAuth } from "@inkeep/agents-core/middleware";

//#region src/domains/manage/routes/projects.ts
const app = new OpenAPIHono();
app.openapi(createProtectedRoute({
	method: "get",
	path: "/",
	summary: "List Projects",
	description: "List all projects within a tenant with pagination. When authorization is enabled, only returns projects the user has access to.",
	operationId: "list-projects",
	tags: ["Projects"],
	permission: inheritedManageTenantAuth(),
	request: {
		params: TenantParamsSchema,
		query: PaginationQueryParamsSchema
	},
	responses: {
		200: {
			description: "List of projects retrieved successfully",
			content: { "application/json": { schema: ProjectListResponse } }
		},
		...commonGetErrorResponses
	},
	...speakeasyOffsetLimitPagination
}), async (c) => {
	const configDb = c.get("db");
	const { tenantId } = c.req.valid("param");
	const userId = c.get("userId");
	const tenantRole = c.get("tenantRole") || "member";
	const page = Number(c.req.query("page")) || 1;
	const limit = Math.min(Number(c.req.query("limit")) || 10, 100);
	let accessibleIds;
	if (userId) {
		const result$1 = await listAccessibleProjectIds({
			userId,
			tenantId,
			orgRole: tenantRole
		});
		if (result$1 !== "all") accessibleIds = result$1;
	}
	const result = await listProjectsWithMetadataPaginated(runDbClient_default, configDb)({
		tenantId,
		pagination: {
			page,
			limit
		},
		projectIds: accessibleIds
	});
	const transformedData = result.data.map((project) => ({
		id: project.id,
		tenantId: project.tenantId,
		name: project.name ?? project.id,
		description: project.description,
		models: project.models,
		stopWhen: project.stopWhen,
		createdAt: project.createdAt,
		updatedAt: project.configUpdatedAt ?? project.createdAt
	}));
	return c.json({
		data: transformedData,
		pagination: result.pagination
	});
});
app.openapi(createProtectedRoute({
	method: "get",
	path: "/{id}",
	summary: "Get Project",
	description: "Get a single project by ID",
	operationId: "get-project-by-id",
	tags: ["Projects"],
	permission: requireProjectPermission("view"),
	request: { params: TenantIdParamsSchema },
	responses: {
		200: {
			description: "Project found",
			content: { "application/json": { schema: ProjectResponse } }
		},
		...commonGetErrorResponses
	}
}), async (c) => {
	const db = c.get("db");
	const { tenantId, id } = c.req.valid("param");
	const project = await getProject(db)({ scopes: {
		tenantId,
		projectId: id
	} });
	if (!project) throw createApiError({
		code: "not_found",
		message: "Project not found"
	});
	return c.json({ data: project });
});
app.openapi(createProtectedRoute({
	method: "post",
	path: "/",
	summary: "Create Project",
	description: "Create a new project. When authorization is enabled, the creator is automatically granted admin role.",
	operationId: "create-project",
	tags: ["Projects"],
	permission: requirePermission({ project: ["create"] }),
	entitlement: requireEntitlement({
		resourceType: QUOTA_RESOURCE_TYPES.PROJECT,
		countFn: (tenantId) => countProjectsInRuntime(runDbClient_default)({ tenantId }),
		label: "Project"
	}),
	request: {
		params: TenantParamsSchema,
		body: { content: { "application/json": { schema: ProjectApiInsertSchema } } }
	},
	responses: {
		201: {
			description: "Project created successfully",
			content: { "application/json": { schema: ProjectResponse } }
		},
		409: {
			description: "Project already exists",
			content: { "application/json": { schema: ErrorResponseSchema } }
		},
		...commonGetErrorResponses
	}
}), async (c) => {
	const configDb = c.get("db");
	const userId = c.get("userId");
	const { tenantId } = c.req.valid("param");
	const body = c.req.valid("json");
	try {
		const runtimeProject = await createProjectMetadataAndBranch(runDbClient_default, configDb)({
			tenantId,
			projectId: body.id,
			createdBy: userId
		});
		const projectMainBranch = getProjectMainBranchName(tenantId, body.id);
		await doltCheckout(configDb)({ branch: projectMainBranch });
		const newResolvedRef = {
			type: "branch",
			name: projectMainBranch,
			hash: ""
		};
		c.set("resolvedRef", newResolvedRef);
		const projectConfig = await createProject(configDb)({
			tenantId,
			...body
		});
		if (!userId) throw createApiError({
			code: "unauthorized",
			message: "User not found"
		});
		try {
			await syncProjectToSpiceDb({
				tenantId,
				projectId: body.id,
				creatorUserId: userId
			});
		} catch (syncError) {
			console.warn("Failed to sync project to SpiceDB:", syncError);
		}
		return c.json({ data: {
			...projectConfig,
			mainBranchName: runtimeProject.mainBranchName
		} }, 201);
	} catch (error) {
		throwIfUniqueConstraintError(error, "Project with this ID already exists");
		throw error;
	}
});
app.openapi(createProtectedRoute({
	method: "patch",
	path: "/{id}",
	summary: "Update Project",
	description: "Update an existing project",
	operationId: "update-project",
	tags: ["Projects"],
	permission: requireProjectPermission("edit"),
	request: {
		params: TenantIdParamsSchema,
		body: { content: { "application/json": { schema: ProjectApiUpdateSchema } } }
	},
	responses: {
		200: {
			description: "Project updated successfully",
			content: { "application/json": { schema: ProjectResponse } }
		},
		...commonGetErrorResponses
	}
}), async (c) => {
	const db = c.get("db");
	const { tenantId, id } = c.req.valid("param");
	const body = c.req.valid("json");
	const project = await updateProject(db)({
		scopes: {
			tenantId,
			projectId: id
		},
		data: body
	});
	if (!project) throw createApiError({
		code: "not_found",
		message: "Project not found"
	});
	return c.json({ data: project });
});
app.openapi(createProtectedRoute({
	method: "delete",
	path: "/{id}",
	summary: "Delete Project",
	description: "Delete a project and its branch. Must be called from the main branch.",
	operationId: "delete-project",
	tags: ["Projects"],
	permission: requirePermission({ project: ["delete"] }),
	request: { params: TenantIdParamsSchema },
	responses: {
		204: { description: "Project deleted successfully" },
		409: {
			description: "Cannot delete project with existing resources",
			content: { "application/json": { schema: ErrorResponseSchema } }
		},
		...commonGetErrorResponses
	}
}), async (c) => {
	const configDb = c.get("db");
	const resolvedRef = c.get("resolvedRef");
	const { tenantId, id } = c.req.valid("param");
	const expectedMainBranch = `${tenantId}_${id}_main`;
	if (resolvedRef?.name !== expectedMainBranch) throw createApiError({
		code: "bad_request",
		message: "Project deletion must be performed from the main branch"
	});
	try {
		await cascadeDeleteByProject(runDbClient_default)({
			scopes: {
				tenantId,
				projectId: id
			},
			fullBranchName: resolvedRef.name
		});
		await deleteProject(configDb)({ scopes: {
			tenantId,
			projectId: id
		} });
		await doltCheckout(configDb)({ branch: "main" });
		if (!await deleteProjectWithBranch(runDbClient_default, manageDbClient_default)({
			tenantId,
			projectId: id
		})) throw createApiError({
			code: "not_found",
			message: "Project not found"
		});
		try {
			await removeProjectFromSpiceDb({
				tenantId,
				projectId: id
			});
		} catch (syncError) {
			console.warn("Failed to remove project from SpiceDB:", syncError);
		}
		return c.body(null, 204);
	} catch (error) {
		if (error.message?.includes("Cannot delete project with existing resources")) throw createApiError({
			code: "conflict",
			message: "Cannot delete project with existing resources"
		});
		throw error;
	}
});
var projects_default = app;

//#endregion
export { projects_default as default };