"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
  if (from && typeof from === "object" || typeof from === "function") {
    for (let key of __getOwnPropNames(from))
      if (!__hasOwnProp.call(to, key) && key !== except)
        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  }
  return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var lock_utils_exports = {};
__export(lock_utils_exports, {
  acquireDirectoryLock: () => acquireDirectoryLock
});
module.exports = __toCommonJS(lock_utils_exports);
var import_general = require("@matter/general");
var import_node_crypto = require("node:crypto");
var import_node_fs = require("node:fs");
var import_promises = require("node:fs/promises");
var import_node_path = require("node:path");
/**
 * @license
 * Copyright 2022-2026 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
const logger = import_general.Logger.get("NodeJsDirectoryLock");
const LOCK_FILE = "matter.lock";
const PID_FILE = "matter.pid";
const PROCESS_TOKEN = (0, import_node_crypto.randomBytes)(8).toString("hex");
const activeLocks = /* @__PURE__ */ new Set();
let exitHandlerInstalled = false;
function trackLock(lock) {
  activeLocks.add(lock);
  if (!exitHandlerInstalled) {
    exitHandlerInstalled = true;
    process.on("exit", removeOrphanedLocks);
  }
}
function removeOrphanedLocks() {
  for (const { lockPath, pidPath, name } of activeLocks) {
    try {
      logger.warn(
        `Storage "${name}" was not closed before process exit; removing orphaned lock.`,
        "Please close any opened storage during shutdown."
      );
    } catch {
    }
    unlinkSyncSafe(lockPath);
    unlinkSyncSafe(pidPath);
  }
}
function unlinkSyncSafe(path) {
  try {
    (0, import_node_fs.unlinkSync)(path);
  } catch {
  }
}
async function acquireDirectoryLock(dirPath, dirName) {
  const lockPath = (0, import_node_path.resolve)(dirPath, LOCK_FILE);
  const pidPath = (0, import_node_path.resolve)(dirPath, PID_FILE);
  if (!await dirExists(dirPath)) {
    return async () => {
    };
  }
  await acquireLock(lockPath, pidPath, dirName);
  await (0, import_promises.writeFile)(pidPath, `${process.pid} ${PROCESS_TOKEN}`);
  const tracked = { lockPath, pidPath, name: dirName };
  trackLock(tracked);
  logger.debug("Acquired storage lock for", dirName, "pid", process.pid);
  return async () => {
    await safeUnlink(lockPath);
    await safeUnlink(pidPath);
    activeLocks.delete(tracked);
    logger.debug("Released storage lock for", dirName);
  };
}
async function acquireLock(lockPath, pidPath, dirName) {
  try {
    const fd = await (0, import_promises.open)(lockPath, "wx");
    await fd.close();
  } catch (error) {
    if (error.code !== "EEXIST") {
      throw error;
    }
    const info = await readLockInfo(pidPath);
    const reason = staleReason(info);
    if (reason !== void 0) {
      logger.info("Cleaning stale storage lock for", dirName, "-", reason);
      await safeUnlink(pidPath);
      await safeUnlink(lockPath);
      try {
        const fd = await (0, import_promises.open)(lockPath, "wx");
        await fd.close();
      } catch (retryError) {
        if (retryError.code === "EEXIST") {
          throw new import_general.StorageLockError("Storage is locked by another process (lock reclaimed during retry)");
        }
        throw retryError;
      }
    } else if (info?.pid === process.pid) {
      throw new import_general.StorageLockError("Storage is already locked by this process");
    } else {
      throw new import_general.StorageLockError(`Storage is locked by another process (pid ${info?.pid})`);
    }
  }
}
function staleReason(info) {
  if (info === void 0) {
    return "no PID file (owner crashed before writing it)";
  }
  if (info.pid === process.pid) {
    return info.token !== PROCESS_TOKEN ? `PID ${info.pid} reused (token mismatch)` : void 0;
  }
  try {
    process.kill(info.pid, 0);
    return void 0;
  } catch (error) {
    if (error.code === "ESRCH") {
      return `owner process ${info.pid} no longer exists`;
    }
    return void 0;
  }
}
async function readLockInfo(pidPath) {
  try {
    const content = await (0, import_promises.readFile)(pidPath, "utf-8");
    const parts = content.trim().split(/\s+/);
    const pid = parseInt(parts[0], 10);
    if (!Number.isFinite(pid) || pid <= 0) {
      return void 0;
    }
    return { pid, token: parts[1] };
  } catch {
    return void 0;
  }
}
async function dirExists(path) {
  try {
    const s = await (0, import_promises.stat)(path);
    return s.isDirectory();
  } catch (e) {
    if (e.code === "ENOENT") {
      return false;
    }
    throw e;
  }
}
async function safeUnlink(path) {
  try {
    await (0, import_promises.unlink)(path);
  } catch (error) {
    if (error.code !== "ENOENT") {
      throw error;
    }
  }
}
//# sourceMappingURL=lock-utils.js.map
