import { literalUnion } from "../../../shared/src/valita.js";
import { Database } from "../../../zqlite/src/db.js";
import { deleteLiteDB } from "../db/delete-lite-db.js";
import { upgradeReplica } from "../services/change-source/common/replica-schema.js";
import { Notifier } from "../services/replicator/notifier.js";
import { getAscendingEvents, recordEvent } from "../services/replicator/schema/replication-state.js";
const replicaFileModeSchema = literalUnion(
  "serving",
  "serving-copy",
  "backup"
);
function replicaFileName(replicaFile, mode) {
  return mode === "serving-copy" ? `${replicaFile}-serving-copy` : replicaFile;
}
const MILLIS_PER_HOUR = 1e3 * 60 * 60;
const MB = 1024 * 1024;
async function connect(lc, { file, vacuumIntervalHours }, walMode, mode) {
  const replica = new Database(lc, file);
  await upgradeReplica(lc, `${mode}-replica`, file);
  replica.pragma("journal_mode = delete");
  const [{ page_size: pageSize }] = replica.pragma(
    "page_size"
  );
  const [{ page_count: pageCount }] = replica.pragma(
    "page_count"
  );
  const [{ freelist_count: freelistCount }] = replica.pragma("freelist_count");
  const dbSize = (pageCount * pageSize / MB).toFixed(2);
  const freelistSize = (freelistCount * pageSize / MB).toFixed(2);
  lc.info?.(`Size of db ${file}: ${dbSize} MB (${freelistSize} MB freeable)`);
  const events = getAscendingEvents(replica);
  lc.debug?.(`Runtime events for db ${file}`, { events });
  if (vacuumIntervalHours !== void 0) {
    const millisSinceLastEvent = Date.now() - (events.at(-1)?.timestamp.getTime() ?? 0);
    if (millisSinceLastEvent / MILLIS_PER_HOUR > vacuumIntervalHours) {
      lc.info?.(`Performing maintenance cleanup on ${file}`);
      const t0 = performance.now();
      replica.unsafeMode(true);
      replica.pragma("journal_mode = OFF");
      replica.exec("VACUUM");
      recordEvent(replica, "vacuum");
      replica.unsafeMode(false);
      const t1 = performance.now();
      lc.info?.(`VACUUM completed (${t1 - t0} ms)`);
    }
  }
  lc.info?.(`setting ${file} to ${walMode} mode`);
  replica.pragma(`journal_mode = ${walMode}`);
  replica.pragma("busy_timeout = 30000");
  replica.pragma("optimize = 0x10002");
  replica.pragma("analysis_limit = 1000");
  lc.info?.(`optimized ${file}`);
  return replica;
}
async function setupReplica(lc, mode, replicaOptions) {
  lc.info?.(`setting up ${mode} replica`);
  switch (mode) {
    case "backup": {
      const replica = await connect(lc, replicaOptions, "wal", mode);
      replica.pragma("wal_autocheckpoint = 0");
      return replica;
    }
    case "serving-copy": {
      const { file } = replicaOptions;
      const copyLocation = replicaFileName(file, mode);
      deleteLiteDB(copyLocation);
      const start = Date.now();
      lc.info?.(`copying ${file} to ${copyLocation}`);
      const replica = new Database(lc, file);
      replica.prepare(`VACUUM INTO ?`).run(copyLocation);
      replica.close();
      lc.info?.(`finished copy (${Date.now() - start} ms)`);
      return connect(lc, { ...replicaOptions, file: copyLocation }, "wal2", mode);
    }
    case "serving":
      return connect(lc, replicaOptions, "wal2", mode);
    default:
      throw new Error(`Invalid ReplicaMode ${mode}`);
  }
}
function setUpMessageHandlers(lc, replicator, parent) {
  handleSubscriptionsFrom(lc, parent, replicator);
}
function handleSubscriptionsFrom(lc, subscriber, notifier) {
  subscriber.onMessageType("subscribe", async () => {
    const subscription = notifier.subscribe();
    subscriber.on("close", () => {
      lc.debug?.(`closing replication subscription from ${subscriber.pid}`);
      subscription.cancel();
    });
    for await (const msg of subscription) {
      subscriber.send(["notify", msg]);
    }
  });
}
function createNotifierFrom(_lc, source) {
  const notifier = new Notifier();
  source.onMessageType(
    "notify",
    (msg) => notifier.notifySubscribers(msg)
  );
  return notifier;
}
function subscribeTo(_lc, source) {
  source.send(["subscribe", {}]);
}
export {
  createNotifierFrom,
  handleSubscriptionsFrom,
  replicaFileModeSchema,
  replicaFileName,
  setUpMessageHandlers,
  setupReplica,
  subscribeTo
};
//# sourceMappingURL=replicator.js.map
