import { tmpdir } from 'node:os';
import path from 'node:path';
import { pid } from 'node:process';
import { assert } from "../../../shared/src/asserts.js";
import { must } from "../../../shared/src/must.js";
import { randInt } from "../../../shared/src/rand.js";
import * as v from "../../../shared/src/valita.js";
import { DatabaseStorage } from "../../../zqlite/src/database-storage.js";
import { getNormalizedZeroConfig } from "../config/zero-config.js";
import { CustomQueryTransformer } from "../custom-queries/transform-query.js";
import { warmupConnections } from "../db/warmup.js";
import { initEventSink } from "../observability/events.js";
import { exitAfter, runUntilKilled } from "../services/life-cycle.js";
import { MutagenService } from "../services/mutagen/mutagen.js";
import { PusherService } from "../services/mutagen/pusher.js";
import { DrainCoordinator } from "../services/view-syncer/drain-coordinator.js";
import { PipelineDriver } from "../services/view-syncer/pipeline-driver.js";
import { Snapshotter } from "../services/view-syncer/snapshotter.js";
import { ViewSyncerService } from "../services/view-syncer/view-syncer.js";
import { pgClient } from "../types/pg.js";
import { parentWorker, singleProcessMode, } from "../types/processes.js";
import { getShardID } from "../types/shards.js";
import { Subscription } from "../types/subscription.js";
import { replicaFileModeSchema, replicaFileName } from "../workers/replicator.js";
import { Syncer } from "../workers/syncer.js";
import { startAnonymousTelemetry } from "./anonymous-otel-start.js";
import { InspectorDelegate } from "./inspector-delegate.js";
import { createLogContext } from "./logging.js";
import { startOtelAuto } from "./otel-start.js";
function randomID() {
    return randInt(1, Number.MAX_SAFE_INTEGER).toString(36);
}
export default 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 = v.parse(args[0], replicaFileModeSchema);
    const { cvr, upstream } = config;
    assert(cvr.maxConnsPerWorker, 'cvr.maxConnsPerWorker must be set');
    assert(upstream.maxConnsPerWorker, 'upstream.maxConnsPerWorker must be set');
    const replicaFile = replicaFileName(config.replica.file, fileMode);
    lc.debug?.(`running view-syncer on ${replicaFile}`);
    const cvrDB = pgClient(lc, cvr.db, {
        max: cvr.maxConnsPerWorker,
        connection: { ['application_name']: `zero-sync-worker-${pid}-cvr` },
    });
    const upstreamDB = pgClient(lc, upstream.db, {
        max: upstream.maxConnsPerWorker,
        connection: { ['application_name']: `zero-sync-worker-${pid}-upstream` },
    });
    const dbWarmup = Promise.allSettled([
        warmupConnections(lc, cvrDB, 'cvr'),
        warmupConnections(lc, upstreamDB, 'upstream'),
    ]);
    const tmpDir = config.storageDBTmpDir ?? tmpdir();
    const operatorStorage = DatabaseStorage.create(lc, path.join(tmpDir, `sync-worker-${pid}-${randInt(1000000, 9999999)}`));
    const shard = getShardID(config);
    const viewSyncerFactory = (id, sub, drainCoordinator) => {
        const logger = lc
            .withContext('component', 'view-syncer')
            .withContext('clientGroupID', id)
            .withContext('instance', randomID());
        lc.debug?.(`creating view syncer`);
        // Create the custom query transformer if configured
        const { getQueries } = config;
        const customQueryTransformer = getQueries.url &&
            new CustomQueryTransformer(logger, { url: getQueries.url, forwardCookies: getQueries.forwardCookies }, shard);
        const inspectorDelegate = new InspectorDelegate(customQueryTransformer);
        return new ViewSyncerService(config, logger, shard, config.taskID, id, cvrDB, config.upstream.type === 'pg' ? upstreamDB : undefined, new PipelineDriver(logger, config.log, new Snapshotter(logger, replicaFile, shard), shard, operatorStorage.createClientGroupStorage(id), id, inspectorDelegate), sub, drainCoordinator, config.log.slowHydrateThreshold, inspectorDelegate, customQueryTransformer);
    };
    const mutagenFactory = (id) => new MutagenService(lc.withContext('component', 'mutagen').withContext('clientGroupID', id), shard, id, upstreamDB, config);
    const pusherFactory = config.push.url === undefined && config.mutate.url === undefined
        ? undefined
        : (id) => new PusherService(upstreamDB, config, {
            ...config.push,
            ...config.mutate,
            url: must(config.push.url ?? config.mutate.url, 'No push or mutate URL configured'),
        }, lc.withContext('clientGroupID', id), id);
    const syncer = new Syncer(lc, config, viewSyncerFactory, mutagenFactory, pusherFactory, parent);
    startAnonymousTelemetry(lc, config);
    void dbWarmup.then(() => parent.send(['ready', { ready: true }]));
    return runUntilKilled(lc, parent, syncer);
}
// fork()
if (!singleProcessMode()) {
    void exitAfter(() => runWorker(must(parentWorker), process.env, ...process.argv.slice(2)));
}
//# sourceMappingURL=syncer.js.map