import { createSilentLogContext } from "../../../../shared/src/logging-test-utils.js";
import { listIndexes, computeZqlSpecs } from "../../db/lite-tables.js";
import { makeErrorDetails, publishCriticalEvent, publishEvent } from "../../observability/events.js";
const byKeys = (a, b) => a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0;
class ReplicationStatusPublisher {
  #db;
  #timer;
  constructor(db) {
    this.#db = db;
  }
  publish(lc, stage, description, interval = 0) {
    this.stop();
    publishEvent(
      lc,
      replicationStatusEvent(lc, this.#db, stage, "OK", description)
    );
    if (interval) {
      this.#timer = setInterval(
        () => this.publish(lc, stage, description, interval),
        interval
      );
    }
    return this;
  }
  async publishAndThrowError(lc, stage, e) {
    this.stop();
    const event = replicationStatusError(lc, stage, "ERROR", this.#db);
    await publishCriticalEvent(lc, event);
    throw e;
  }
  stop() {
    clearInterval(this.#timer);
    return this;
  }
}
async function publishReplicationError(lc, stage, description, errorDetails, now = /* @__PURE__ */ new Date()) {
  const event = {
    type: "zero/events/status/replication/v1",
    component: "replication",
    status: "ERROR",
    stage,
    description,
    errorDetails,
    time: now.toISOString()
  };
  await publishCriticalEvent(lc, event);
}
function replicationStatusError(lc, stage, e, db, now = /* @__PURE__ */ new Date()) {
  const event = replicationStatusEvent(lc, db, stage, "ERROR", String(e), now);
  event.errorDetails = makeErrorDetails(e);
  return event;
}
function replicationStatusEvent(lc, db, stage, status, description, now = /* @__PURE__ */ new Date()) {
  try {
    return {
      type: "zero/events/status/replication/v1",
      component: "replication",
      status,
      stage,
      description,
      time: now.toISOString(),
      state: {
        tables: db ? getReplicatedTables(db) : [],
        indexes: db ? getReplicatedIndexes(db) : [],
        replicaSize: db ? getReplicaSize(db) : void 0
      }
    };
  } catch (e) {
    lc.warn?.(`Unable to create full ReplicationStatusEvent`, e);
    return {
      type: "zero/events/status/replication/v1",
      component: "replication",
      status,
      stage,
      description,
      time: now.toISOString(),
      state: {
        tables: [],
        indexes: [],
        replicaSize: 0
      }
    };
  }
}
function getReplicatedTables(db) {
  const fullTables = /* @__PURE__ */ new Map();
  const clientSchema = computeZqlSpecs(
    createSilentLogContext(),
    // avoid logging warnings about indexes
    db,
    // TODO: Consider exposing backfilling columns with an indication
    //       of backfill status.
    { includeBackfillingColumns: false },
    /* @__PURE__ */ new Map(),
    fullTables
  );
  return [...fullTables.entries()].sort(byKeys).map(([table, spec]) => ({
    table,
    columns: Object.entries(spec.columns).sort(byKeys).map(([column, spec2]) => ({
      column,
      upstreamType: spec2.dataType.split("|")[0],
      clientType: clientSchema.get(table)?.zqlSpec[column]?.type ?? null
    }))
  }));
}
function getReplicatedIndexes(db) {
  return listIndexes(db).map(({ tableName: table, columns, unique }) => ({
    table,
    unique,
    columns: Object.entries(columns).sort(byKeys).map(([column, dir]) => ({ column, dir }))
  }));
}
function getReplicaSize(db) {
  const [{ page_count: pageCount }] = db.pragma(
    "page_count"
  );
  const [{ page_size: pageSize }] = db.pragma("page_size");
  return pageCount * pageSize;
}
export {
  ReplicationStatusPublisher,
  publishReplicationError,
  replicationStatusError,
  replicationStatusEvent
};
//# sourceMappingURL=replication-status.js.map
