import { assert } from "../../../shared/src/asserts.js";
import { promiseVoid } from "../../../shared/src/resolved-promises.js";
import { parse } from "../../../shared/src/valita.js";
import { must } from "../../../shared/src/must.js";
import { parentWorker, singleProcessMode } from "../types/processes.js";
import { exitAfter, runUntilKilled } from "../services/life-cycle.js";
import { getShardID } from "../types/shards.js";
import { getNormalizedZeroConfig } from "../config/zero-config.js";
import { startOtelAuto } from "./otel-start.js";
import { createLogContext } from "./logging.js";
import { randInt } from "../../../shared/src/rand.js";
import { pgClient } from "../types/pg.js";
import { Snapshotter } from "../services/view-syncer/snapshotter.js";
import { PipelineDriver } from "../services/view-syncer/pipeline-driver.js";
import { warmupConnections } from "../db/warmup.js";
import { initEventSink } from "../observability/events.js";
import { replicaFileModeSchema, replicaFileName } from "../workers/replicator.js";
import { startAnonymousTelemetry } from "./anonymous-otel-start.js";
import { DatabaseStorage } from "../../../zqlite/src/database-storage.js";
import { ProtocolErrorWithLevel } from "../types/error-with-level.js";
import { CustomQueryTransformer } from "../custom-queries/transform-query.js";
import { MutagenService } from "../services/mutagen/mutagen.js";
import { PusherService } from "../services/mutagen/pusher.js";
import { ConnectionContextManagerImpl } from "../services/view-syncer/connection-context-manager.js";
import { ViewSyncerService } from "../services/view-syncer/view-syncer.js";
import { tokenConfigOptions, verifyToken } from "../auth/jwt.js";
import { Syncer } from "../workers/syncer.js";
import { InspectorDelegate } from "./inspector-delegate.js";
import { isPriorityOpRunning, runPriorityOp } from "./priority-op.js";
import { pid } from "node:process";
import { tmpdir } from "node:os";
import { randomUUID } from "node:crypto";
import path from "node:path";
//#region ../zero-cache/src/server/syncer.ts
function randomID() {
	return randInt(1, Number.MAX_SAFE_INTEGER).toString(36);
}
function getCustomQueryConfig(config) {
	const queryConfig = config.query?.url ? config.query : config.getQueries;
	if (!queryConfig?.url) return;
	return {
		url: queryConfig.url,
		apiKey: queryConfig.apiKey,
		allowedClientHeaders: queryConfig.allowedClientHeaders,
		forwardCookies: queryConfig.forwardCookies ?? false
	};
}
function runWorker(parent, env, ...args) {
	const config = getNormalizedZeroConfig({
		env,
		argv: args.slice(1)
	});
	startOtelAuto(createLogContext(config, { worker: "syncer" }, false));
	const lc = createLogContext(config, { worker: "syncer" }, true);
	initEventSink(lc, config);
	assert(args.length > 0, `replicator mode not specified`);
	const fileMode = parse(args[0], replicaFileModeSchema);
	const { cvr, upstream, enableCrudMutations } = config;
	const replicaFile = replicaFileName(config.replica.file, fileMode);
	lc.debug?.(`running view-syncer on ${replicaFile}`);
	const cvrDB = pgClient(lc, cvr.db, {
		max: must(cvr.maxConnsPerWorker, "cvr.maxConnsPerWorker must be set"),
		connection: { ["application_name"]: `zero-sync-worker-${pid}-cvr` }
	});
	const upstreamDB = enableCrudMutations ? pgClient(lc, upstream.db, {
		max: must(upstream.maxConnsPerWorker, "upstream.maxConnsPerWorker must be set"),
		connection: { ["application_name"]: `zero-sync-worker-${pid}-upstream` }
	}) : void 0;
	const dbWarmup = Promise.allSettled([warmupConnections(lc, cvrDB, "cvr"), upstreamDB ? warmupConnections(lc, upstreamDB, "upstream") : promiseVoid]);
	const tmpDir = config.storageDBTmpDir ?? tmpdir();
	const operatorStorage = DatabaseStorage.create(lc, path.join(tmpDir, `sync-worker-${randomUUID()}`));
	const writeAuthzStorage = DatabaseStorage.create(lc, path.join(tmpDir, `mutagen-${randomUUID()}`));
	const shard = getShardID(config);
	const customQueryConfig = getCustomQueryConfig(config);
	const pushConfig = config.push.url === void 0 && config.mutate.url === void 0 ? void 0 : {
		...config.push,
		...config.mutate,
		url: must(config.push.url ?? config.mutate.url, "No push or mutate URL configured")
	};
	/** @deprecated used in JWT validation */
	let validateLegacyJWT = void 0;
	if (tokenConfigOptions(config.auth ?? {}).length === 1) validateLegacyJWT = async (token, { userID }) => {
		if (!userID) throw new ProtocolErrorWithLevel({
			kind: "Unauthorized",
			message: "UserID is required for JWT validation.",
			origin: "zeroCache"
		}, "warn");
		return {
			type: "jwt",
			raw: token,
			decoded: await verifyToken(config.auth, token, {
				subject: userID,
				...config.auth?.issuer && { issuer: config.auth.issuer },
				...config.auth?.audience && { audience: config.auth.audience }
			})
		};
	};
	const viewSyncerFactory = (id, sub, drainCoordinator) => {
		const logger = lc.withContext("component", "view-syncer").withContext("clientGroupID", id).withContext("instance", randomID());
		const customQueryTransformer = customQueryConfig && new CustomQueryTransformer(logger, shard);
		const contextManager = new ConnectionContextManagerImpl(logger, config.auth.revalidateIntervalSeconds, config.auth.retransformIntervalSeconds, customQueryConfig, pushConfig, validateLegacyJWT);
		lc.debug?.(`creating view syncer. Query Planner Enabled: ${config.enableQueryPlanner}`);
		const inspectorDelegate = new InspectorDelegate(customQueryTransformer);
		const priorityOpRunningYieldThresholdMs = Math.max(config.yieldThresholdMs / 4, 2);
		const normalYieldThresholdMs = Math.max(config.yieldThresholdMs, 2);
		return new ViewSyncerService(config, logger, shard, config.taskID, id, cvrDB, new PipelineDriver(logger, config.log, new Snapshotter(logger, replicaFile, shard), shard, operatorStorage.createClientGroupStorage(id), id, inspectorDelegate, () => isPriorityOpRunning() ? priorityOpRunningYieldThresholdMs : normalYieldThresholdMs, config.enableQueryPlanner, config), sub, drainCoordinator, config.log.slowHydrateThreshold, inspectorDelegate, contextManager, customQueryTransformer, runPriorityOp);
	};
	const syncer = new Syncer(lc, config, viewSyncerFactory, upstreamDB ? (id) => new MutagenService(lc.withContext("component", "mutagen").withContext("clientGroupID", id), shard, id, upstreamDB, config, writeAuthzStorage) : void 0, pushConfig === void 0 ? void 0 : (id, contextManager) => new PusherService(config, lc.withContext("clientGroupID", id), id, contextManager), parent, validateLegacyJWT);
	startAnonymousTelemetry(lc, config);
	dbWarmup.then(() => parent.send(["ready", { ready: true }]));
	return runUntilKilled(lc, parent, syncer);
}
if (!singleProcessMode()) exitAfter(() => runWorker(must(parentWorker), process.env, ...process.argv.slice(2)));
//#endregion
export { runWorker as default };

//# sourceMappingURL=syncer.js.map