/**
 * @license
 * Copyright 2022-2026 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
import { Mutex } from "#util/Mutex.js";
import {
  MAX_SEGMENT_LINES,
  isCompressedSegmentFile,
  parseSegmentFilename,
  segmentFilename,
  serializeCommit
} from "./WalCommit.js";
class WalWriter {
  #walDir;
  #name;
  #maxSegmentSize;
  #fsync;
  #onRotate;
  #mutex = new Mutex(this);
  #handle;
  #currentSegment = 0;
  #currentOffset = 0;
  #currentSize = 0;
  constructor(walDir, options) {
    this.#walDir = walDir;
    this.#name = options?.name ?? "unknown";
    this.#maxSegmentSize = options?.maxSegmentSize ?? 16 * 1024 * 1024;
    this.#fsync = options?.fsync ?? true;
    this.#onRotate = options?.onRotate;
  }
  /**
   * The segment number currently being written to.
   */
  get currentSegment() {
    return this.#currentSegment;
  }
  /**
   * Write a commit to the WAL, returning its commit ID and timestamp.
   */
  async write(ops) {
    return this.#mutex.produce(async () => {
      if (!this.#handle) {
        await this.#openSegment();
      }
      if (this.#currentSize > 0 && (this.#currentSize >= this.#maxSegmentSize || this.#currentOffset >= MAX_SEGMENT_LINES)) {
        await this.#rotate();
      }
      const ts = Date.now();
      const line = serializeCommit({ ts, ops }) + "\n";
      const id = { segment: this.#currentSegment, offset: this.#currentOffset };
      await this.#handle.writeHandle(line);
      if (this.#fsync) {
        await this.#handle.fsync();
      }
      this.#currentOffset++;
      this.#currentSize += new TextEncoder().encode(line).length;
      return { id, ts };
    });
  }
  /**
   * Close the current file handle.
   */
  async close() {
    await this.#mutex.close();
    if (this.#handle) {
      await this.#handle.close();
      this.#handle = void 0;
    }
  }
  async #openSegment() {
    await this.#walDir.mkdir();
    let maxSegment = 0;
    let maxSegmentCompressed = false;
    if (await this.#walDir.exists()) {
      for await (const entry of this.#walDir.entries()) {
        if (entry.kind !== "file") continue;
        const seg = parseSegmentFilename(entry.name);
        if (seg !== void 0) {
          if (seg > maxSegment) {
            maxSegment = seg;
            maxSegmentCompressed = isCompressedSegmentFile(entry.name);
          } else if (seg === maxSegment && isCompressedSegmentFile(entry.name)) {
            maxSegmentCompressed = true;
          }
        }
      }
    }
    if (maxSegment > 0) {
      if (maxSegmentCompressed) {
        this.#currentSegment = maxSegment + 1;
      } else {
        const lastFile = this.#walDir.file(segmentFilename(maxSegment));
        let lineCount = 0;
        let byteSize = 0;
        for await (const line of lastFile.readText({ lines: true })) {
          if (line.trim() !== "") {
            lineCount++;
          }
          byteSize += new TextEncoder().encode(line + "\n").length;
        }
        if (byteSize < this.#maxSegmentSize && lineCount < MAX_SEGMENT_LINES) {
          this.#currentSegment = maxSegment;
          this.#currentOffset = lineCount;
          this.#currentSize = byteSize;
          this.#handle = await lastFile.open(this.#handlePurpose, "a");
          return;
        }
        this.#currentSegment = maxSegment + 1;
      }
    } else {
      this.#currentSegment = 1;
    }
    this.#currentOffset = 0;
    this.#currentSize = 0;
    const file = this.#walDir.file(segmentFilename(this.#currentSegment));
    this.#handle = await file.open(this.#handlePurpose, "a");
  }
  async #rotate() {
    if (this.#handle) {
      await this.#handle.close();
    }
    const closedSegment = this.#currentSegment;
    this.#currentSegment++;
    this.#currentOffset = 0;
    this.#currentSize = 0;
    const file = this.#walDir.file(segmentFilename(this.#currentSegment));
    this.#handle = await file.open(this.#handlePurpose, "a");
    this.#onRotate?.(closedSegment);
  }
  get #handlePurpose() {
    return `WAL writer ${this.#name} segment ${this.#currentSegment}`;
  }
}
export {
  WalWriter
};
//# sourceMappingURL=WalWriter.js.map
