import { parse } from "../../../shared/src/valita.js";
import { getErrorDetails, getErrorMessage } from "../../../shared/src/error.js";
import { mapAST } from "../../../zero-protocol/src/ast.js";
import { TransformFailed } from "../../../zero-protocol/src/error-kind-enum.js";
import { Server } from "../../../zero-protocol/src/error-origin-enum.js";
import { Internal, Parse } from "../../../zero-protocol/src/error-reason-enum.js";
import { transformRequestMessageSchema } from "../../../zero-protocol/src/custom-queries.js";
import { asQueryInternals } from "../../../zql/src/query/query-internals.js";
import { clientToServer } from "../../../zero-schema/src/name-mapper.js";
import { QueryParseError } from "../../../zql/src/query/error.js";
import { createLogContext } from "../logging.js";
//#region ../zero-server/src/queries/process-queries.ts
/**
* Invokes the callback `cb` for each query in the request or JSON body.
* The callback should return a Query or Promise<Query> that is the transformed result.
*
* This function will call `cb` in parallel for each query found in the request.
*
* If you need to limit concurrency, you can use a library like `p-limit` to wrap the `cb` function.
* @deprecated Use {@linkcode handleQueryRequest} instead.
*/
function handleGetQueriesRequest(cb, schema, requestOrJsonBody, logLevel = "info") {
	return transform(cb, schema, requestOrJsonBody, "getQueries", logLevel);
}
/**
* Invokes the callback `cb` for each query in the request or JSON body.
* The callback should return a Query or Promise<Query> that is the transformed result.
*
* This function will call `cb` in parallel for each query found in the request.
*
* If you need to limit concurrency, you can use a library like `p-limit` to wrap the `cb` function.
* @deprecated Use {@linkcode handleQueryRequest} instead.
*/
function handleTransformRequest(cb, schema, requestOrJsonBody, logLevel = "info") {
	return transform(cb, schema, requestOrJsonBody, "transform", logLevel);
}
/**
* Processes a transform request by invoking the provided callback for each query.
* The callback should return a Query that is the transformed result.
*
* This function will call `transformQuery` in parallel for each query found in the request.
*
* @param transformQuery - Callback function that takes a query name and args, and returns a Query
* @param schema - The Zero schema
* @param requestOrJsonBody - Either a Request object or the JSON body directly
* @param logLevel - Logging level (defaults to 'info')
* @returns A Promise that resolves to a TransformResponseMessage
*/
function handleQueryRequest(transformQuery, schema, requestOrJsonBody, logLevel = "info") {
	return transform((name, argsArray) => transformQuery(name, argsArray[0]), schema, requestOrJsonBody, "query", logLevel);
}
async function transform(cb, schema, requestOrJsonBody, apiName, logLevel = "info") {
	const lc = createLogContext(logLevel).withContext("TransformRequest");
	let parsed;
	let queryIDs = [];
	try {
		let body;
		if (requestOrJsonBody instanceof Request) body = await requestOrJsonBody.json();
		else body = requestOrJsonBody;
		parsed = parse(body, transformRequestMessageSchema);
		queryIDs = parsed[1].map((r) => r.id);
	} catch (error) {
		lc.error?.(`Failed to parse ${apiName} request`, error);
		const message = `Failed to parse ${apiName} request: ${getErrorMessage(error)}`;
		const details = getErrorDetails(error);
		return ["transformFailed", {
			kind: TransformFailed,
			origin: Server,
			reason: Parse,
			message,
			queryIDs,
			...details ? { details } : {}
		}];
	}
	try {
		const nameMapper = clientToServer(schema.tables);
		return ["transformed", await Promise.all(parsed[1].map(async (req) => {
			let finalQuery;
			try {
				const result = await cb(req.name, req.args);
				finalQuery = "query" in result ? result.query : result;
			} catch (error) {
				const message = getErrorMessage(error);
				const details = getErrorDetails(error);
				return {
					error: error instanceof QueryParseError ? "parse" : "app",
					id: req.id,
					name: req.name,
					message,
					...details ? { details } : {}
				};
			}
			try {
				const ast = mapAST(asQueryInternals(finalQuery).ast, nameMapper);
				return {
					id: req.id,
					name: req.name,
					ast
				};
			} catch (error) {
				lc.error?.("Failed to map AST", error);
				throw error;
			}
		}))];
	} catch (e) {
		const message = getErrorMessage(e);
		const details = getErrorDetails(e);
		return ["transformFailed", {
			kind: TransformFailed,
			origin: Server,
			reason: Internal,
			message,
			queryIDs,
			...details ? { details } : {}
		}];
	}
}
//#endregion
export { handleGetQueriesRequest, handleQueryRequest, handleTransformRequest };

//# sourceMappingURL=process-queries.js.map