import fs from "node:fs";
import path from "node:path";
import { logger } from "../logger.js";
/**
 * Lightweight PID-based single-instance guard.
 *
 * Writes a lock file containing the current PID to the given directory.
 * If a lock file already exists and the recorded process is still alive,
 * the function throws so the caller can abort startup.
 *
 * Stale locks (process no longer running) are automatically reclaimed.
 */
const LOCK_FILENAME = "visionclaw.pid";
function lockPath(dir) {
    return path.join(dir, LOCK_FILENAME);
}
/** Check whether a process with the given PID is still running. */
function isProcessAlive(pid) {
    try {
        process.kill(pid, 0); // signal 0 = existence check, does not kill
        return true;
    }
    catch {
        return false;
    }
}
/**
 * Acquire a PID lock for the given profile directory.
 *
 * @returns A cleanup function that releases the lock.  The caller should
 *          invoke it during graceful shutdown; it is also registered
 *          automatically via `process.on('exit')` as a safety net.
 * @throws  If another live VisionClaw instance already holds the lock.
 */
export function acquirePidLock(profileDir) {
    const file = lockPath(profileDir);
    // ── Check for an existing lock ──────────────────────────────────────
    if (fs.existsSync(file)) {
        try {
            const raw = fs.readFileSync(file, "utf-8").trim();
            const existingPid = Number.parseInt(raw, 10);
            if (!Number.isNaN(existingPid) && isProcessAlive(existingPid)) {
                throw new Error(`Another VisionClaw instance is already running for this profile (PID: ${existingPid}). ` +
                    `If this is stale, remove ${file} manually.`);
            }
            // Stale lock — reclaim it.
            logger.debug(`Reclaiming stale PID lock (previous PID ${raw} is no longer running)`);
        }
        catch (err) {
            // Re-throw our own "already running" error; swallow fs read errors.
            if (err instanceof Error && err.message.includes("already running")) {
                throw err;
            }
            logger.debug(`Ignoring unreadable PID lock file: ${err instanceof Error ? err.message : String(err)}`);
        }
    }
    // ── Write the current PID ───────────────────────────────────────────
    fs.mkdirSync(profileDir, { recursive: true });
    fs.writeFileSync(file, `${process.pid}\n`, { mode: 0o644 });
    let released = false;
    const release = () => {
        if (released)
            return;
        released = true;
        try {
            // Only remove if it still contains *our* PID (defensive).
            const raw = fs.readFileSync(file, "utf-8").trim();
            if (raw === String(process.pid)) {
                fs.unlinkSync(file);
            }
        }
        catch {
            // Best-effort — the file may already be gone.
        }
    };
    // Safety net: clean up even on unexpected exit.
    process.on("exit", release);
    return release;
}
//# sourceMappingURL=pidlock.js.map