import { randomUUID } from "node:crypto";
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 { parse } from "../../../shared/src/valita.js";
import { DatabaseStorage } from "../../../zqlite/src/database-storage.js";
import { AuthSessionImpl } from "../auth/auth.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 { 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 { singleProcessMode, parentWorker } from "../types/processes.js";
import { getShardID } from "../types/shards.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";
import { runPriorityOp, isPriorityOpRunning } from "./priority-op.js";
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 void 0;
  }
  return {
    url: queryConfig.url,
    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 } = 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-${randomUUID()}`)
  );
  const writeAuthzStorage = DatabaseStorage.create(
    lc,
    path.join(tmpDir, `mutagen-${randomUUID()}`)
  );
  const shard = getShardID(config);
  const viewSyncerFactory = (id, sub, drainCoordinator, validateLegacyJWT) => {
    const logger = lc.withContext("component", "view-syncer").withContext("clientGroupID", id).withContext("instance", randomID());
    lc.debug?.(
      `creating view syncer. Query Planner Enabled: ${config.enableQueryPlanner}`
    );
    const customQueryConfig = getCustomQueryConfig(config);
    const customQueryTransformer = customQueryConfig && new CustomQueryTransformer(logger, customQueryConfig, shard);
    const inspectorDelegate = new InspectorDelegate(customQueryTransformer);
    const priorityOpRunningYieldThresholdMs = Math.max(
      config.yieldThresholdMs / 4,
      2
    );
    const normalYieldThresholdMs = Math.max(config.yieldThresholdMs, 2);
    const authSession = new AuthSessionImpl(
      logger.withContext("component", "auth-session"),
      id,
      validateLegacyJWT
    );
    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,
      customQueryTransformer,
      runPriorityOp,
      authSession
    );
  };
  const mutagenFactory = (id) => new MutagenService(
    lc.withContext("component", "mutagen").withContext("clientGroupID", id),
    shard,
    id,
    upstreamDB,
    config,
    writeAuthzStorage
  );
  const pusherFactory = config.push.url === void 0 && config.mutate.url === void 0 ? void 0 : (id) => new PusherService(
    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);
}
if (!singleProcessMode()) {
  void exitAfter(
    () => runWorker(must(parentWorker), process.env, ...process.argv.slice(2))
  );
}
export {
  runWorker as default
};
//# sourceMappingURL=syncer.js.map
