import { jsonObjectSchema, stringify } from "../../../../../shared/src/bigint-json.js";
import { parse } from "../../../../../shared/src/valita.js";
import { CREATE_CHANGELOG_SCHEMA } from "./change-log.js";
import { CREATE_COLUMN_METADATA_TABLE } from "./column-metadata.js";
import { CREATE_TABLE_METADATA_TABLE } from "./table-metadata.js";
import { array, string, object } from "@badrap/valita";
const CREATE_RUNTIME_EVENTS_TABLE = `
  CREATE TABLE "_zero.runtimeEvents" (
    event TEXT PRIMARY KEY ON CONFLICT REPLACE,
    timestamp TEXT NOT NULL DEFAULT (current_timestamp)
  );
`;
const CREATE_REPLICATION_STATE_SCHEMA = (
  // replicaVersion     : A value identifying the version at which the initial sync happened, i.e.
  //                      the version at which all rows were copied, and to `_0_version` was set.
  //                      This value is used to distinguish data from other replicas (e.g. if a
  //                      replica is reset or if there are ever multiple replicas).
  // publications       : JSON stringified array of publication names
  // initialSyncContext : Metadata related to the context of when and how the replica was initially
  //                      synced. This corresponds with the same column stored in upstream and is
  //                      used for debugging replica version mismatches, which can arise from a number
  //                      of misconfigurations, such as dueling replication-managers, or restores of
  //                      stale litestream backups.
  // lock               : Auto-magic column for enforcing single-row semantics.
  /*sql*/
  `
  CREATE TABLE "_zero.replicationConfig" (
    replicaVersion TEXT NOT NULL,
    publications TEXT NOT NULL,
    initialSyncContext TEXT DEFAULT '{}',
    lock INTEGER PRIMARY KEY DEFAULT 1 CHECK (lock=1)
  );
  
  CREATE TABLE "_zero.replicationState" (
    stateVersion TEXT NOT NULL,
    lock INTEGER PRIMARY KEY DEFAULT 1 CHECK (lock=1)
  );
  ` + CREATE_CHANGELOG_SCHEMA + CREATE_RUNTIME_EVENTS_TABLE + CREATE_COLUMN_METADATA_TABLE + CREATE_TABLE_METADATA_TABLE
);
const stringArray = array(string());
const subscriptionStateSchema = object({
  replicaVersion: string(),
  publications: string(),
  watermark: string()
}).map((s) => ({
  ...s,
  publications: parse(JSON.parse(s.publications), stringArray)
}));
const subscriptionStateAndContextSchema = object({
  replicaVersion: string(),
  publications: string(),
  initialSyncContext: string(),
  watermark: string()
}).map((s) => ({
  ...s,
  publications: parse(JSON.parse(s.publications), stringArray),
  initialSyncContext: parse(
    JSON.parse(s.initialSyncContext),
    jsonObjectSchema
  )
}));
const replicationStateSchema = object({
  stateVersion: string()
});
function initReplicationState(db, publications, watermark, initialSyncContext = {}, createTables = true) {
  if (createTables) {
    createReplicationStateTables(db);
  }
  db.prepare(
    `
    INSERT INTO "_zero.replicationConfig" 
       (replicaVersion, publications, initialSyncContext) VALUES (?, ?, ?)
    `
  ).run(
    watermark,
    JSON.stringify(publications.sort()),
    stringify(initialSyncContext)
  );
  db.prepare(
    `
    INSERT INTO "_zero.replicationState" (stateVersion) VALUES (?)
    `
  ).run(watermark);
  recordEvent(db, "sync");
}
function createReplicationStateTables(db) {
  db.exec(CREATE_REPLICATION_STATE_SCHEMA);
}
function recordEvent(db, event) {
  db.prepare(
    `
    INSERT INTO "_zero.runtimeEvents" (event) VALUES (?) 
    `
  ).run(event);
}
function getAscendingEvents(db) {
  const result = db.prepare(
    `SELECT event, timestamp FROM "_zero.runtimeEvents" 
         ORDER BY timestamp ASC
    `
  ).all();
  return result.map(({ event, timestamp }) => ({
    event,
    timestamp: /* @__PURE__ */ new Date(timestamp + "Z")
  }));
}
function getSubscriptionState(db) {
  const result = db.get(
    /*sql*/
    `
      SELECT c.replicaVersion, c.publications, s.stateVersion as watermark
        FROM "_zero.replicationConfig" as c
        JOIN "_zero.replicationState" as s
        ON c.lock = s.lock
    `
  );
  return parse(result, subscriptionStateSchema);
}
function getSubscriptionStateAndContext(db) {
  const result = db.get(
    /*sql*/
    `
      SELECT c.replicaVersion, c.publications, c.initialSyncContext,
        s.stateVersion as watermark
        FROM "_zero.replicationConfig" as c
        JOIN "_zero.replicationState" as s
        ON c.lock = s.lock
    `
  );
  return parse(result, subscriptionStateAndContextSchema);
}
function updateReplicationWatermark(db, watermark) {
  db.run(`UPDATE "_zero.replicationState" SET stateVersion=?`, watermark);
}
function getReplicationState(db) {
  const result = db.get(`SELECT stateVersion FROM "_zero.replicationState"`);
  return parse(result, replicationStateSchema);
}
export {
  CREATE_RUNTIME_EVENTS_TABLE,
  createReplicationStateTables,
  getAscendingEvents,
  getReplicationState,
  getSubscriptionState,
  getSubscriptionStateAndContext,
  initReplicationState,
  recordEvent,
  updateReplicationWatermark
};
//# sourceMappingURL=replication-state.js.map
