import { assert } from "../../../../shared/src/asserts.js";
import { max } from "../../types/lexi-version.js";
import "./change-streamer.js";
import { Unknown } from "./error-type-enum.js";
class Subscriber {
  #protocolVersion;
  id;
  #downstream;
  #watermark;
  #acked;
  #backlog;
  constructor(protocolVersion, id, watermark, downstream) {
    this.#protocolVersion = protocolVersion;
    this.id = id;
    this.#downstream = downstream;
    this.#watermark = watermark;
    this.#acked = watermark;
    this.#backlog = [];
  }
  get watermark() {
    return this.#watermark;
  }
  get acked() {
    return this.#acked;
  }
  async send(change) {
    const [watermark] = change;
    if (watermark > this.#watermark) {
      if (this.#backlog) {
        this.#backlog.push(change);
      } else {
        await this.#send(change);
      }
    }
  }
  #initialStatusSent = false;
  #ensureInitialStatusSent() {
    if (this.#protocolVersion >= 2 && !this.#initialStatusSent) {
      this.#downstream.push(["status", { tag: "status" }]);
      this.#initialStatusSent = true;
    }
  }
  /** catchup() is called on ChangeEntries loaded from the store. */
  async catchup(change) {
    this.#ensureInitialStatusSent();
    await this.#send(change);
  }
  /**
   * Marks the Subscribe as "caught up" and flushes any backlog of
   * entries that were received during the catchup.
   */
  setCaughtUp() {
    this.#ensureInitialStatusSent();
    assert(
      this.#backlog,
      "setCaughtUp() called but subscriber is not in catchup mode"
    );
    for (const change of this.#backlog) {
      void this.#send(change);
    }
    this.#backlog = null;
  }
  async #send(change) {
    const [watermark, downstream] = change;
    if (watermark <= this.watermark) {
      return;
    }
    if (!this.supportsMessage(downstream[1])) {
      return;
    }
    const pending = this.#downstream.push(downstream);
    if (downstream[0] === "commit") {
      this.#watermark = watermark;
      void pending.result.then((val) => {
        if (val === "consumed") {
          this.#acked = max(this.#acked, watermark);
        }
      });
    }
    await pending.result;
  }
  supportsMessage(change) {
    switch (change.tag) {
      case "update-table-metadata":
        return this.#protocolVersion >= 5;
    }
    return true;
  }
  fail(err) {
    this.close(Unknown, String(err));
  }
  close(error, message) {
    if (error) {
      const { result } = this.#downstream.push(["error", { type: error, message }]);
      void result.then(() => this.#downstream.cancel());
    } else {
      this.#downstream.cancel();
    }
  }
}
export {
  Subscriber
};
//# sourceMappingURL=subscriber.js.map
