import * as fs from "fs";
import * as path from "path";
export class InstanceStateManager {
    stateDir;
    statePath;
    lockPath;
    ownsInstances = false;
    constructor(projectRoot) {
        this.stateDir = path.join(projectRoot, ".specific");
        this.statePath = path.join(this.stateDir, "state.json");
        this.lockPath = path.join(this.stateDir, "state.lock");
    }
    ensureStateDir() {
        if (!fs.existsSync(this.stateDir)) {
            fs.mkdirSync(this.stateDir, { recursive: true });
        }
    }
    isProcessRunning(pid) {
        try {
            process.kill(pid, 0);
            return true;
        }
        catch (e) {
            const err = e;
            // ESRCH means no such process
            return err.code !== "ESRCH";
        }
    }
    async acquireLock(timeoutMs = 5000) {
        this.ensureStateDir();
        const startTime = Date.now();
        while (Date.now() - startTime < timeoutMs) {
            try {
                // Try to create lock file exclusively
                const fd = fs.openSync(this.lockPath, fs.constants.O_CREAT | fs.constants.O_EXCL | fs.constants.O_WRONLY);
                fs.writeSync(fd, String(process.pid));
                fs.closeSync(fd);
                return () => {
                    try {
                        fs.unlinkSync(this.lockPath);
                    }
                    catch {
                        // Ignore errors when removing lock
                    }
                };
            }
            catch (e) {
                const err = e;
                if (err.code === "EEXIST") {
                    // Lock exists, check if owner is still alive
                    try {
                        const lockPid = parseInt(fs.readFileSync(this.lockPath, "utf-8").trim(), 10);
                        if (!this.isProcessRunning(lockPid)) {
                            // Owner is dead, remove stale lock
                            fs.unlinkSync(this.lockPath);
                            continue;
                        }
                    }
                    catch {
                        // Error reading lock, try to remove it
                        try {
                            fs.unlinkSync(this.lockPath);
                        }
                        catch {
                            // Ignore
                        }
                        continue;
                    }
                    // Owner is alive, wait and retry
                    await new Promise((resolve) => setTimeout(resolve, 100));
                }
                else {
                    throw e;
                }
            }
        }
        throw new Error("Failed to acquire state lock (timeout)");
    }
    async getExistingInstances() {
        if (!fs.existsSync(this.statePath)) {
            return null;
        }
        const releaseLock = await this.acquireLock();
        try {
            const content = fs.readFileSync(this.statePath, "utf-8");
            const state = JSON.parse(content);
            // Verify owner is still running
            if (!this.isProcessRunning(state.owner.pid)) {
                return null; // State is stale
            }
            return state;
        }
        catch {
            return null;
        }
        finally {
            releaseLock();
        }
    }
    async cleanStaleState() {
        if (!fs.existsSync(this.statePath)) {
            return false;
        }
        const releaseLock = await this.acquireLock();
        try {
            const content = fs.readFileSync(this.statePath, "utf-8");
            const state = JSON.parse(content);
            if (!this.isProcessRunning(state.owner.pid)) {
                // Owner is dead, clean up state
                fs.unlinkSync(this.statePath);
                return true;
            }
            return false;
        }
        catch {
            // Error reading state, try to clean it
            try {
                fs.unlinkSync(this.statePath);
                return true;
            }
            catch {
                // Ignore
            }
            return false;
        }
        finally {
            releaseLock();
        }
    }
    async claimOwnership(command) {
        const releaseLock = await this.acquireLock();
        try {
            // Check if state already exists
            if (fs.existsSync(this.statePath)) {
                const content = fs.readFileSync(this.statePath, "utf-8");
                const state = JSON.parse(content);
                if (this.isProcessRunning(state.owner.pid)) {
                    throw new Error(`Instances already owned by PID ${state.owner.pid}`);
                }
                // Owner is dead, we can take over
            }
            const state = {
                version: 1,
                owner: {
                    pid: process.pid,
                    command,
                    startedAt: new Date().toISOString(),
                },
                databases: {},
                services: {},
            };
            this.writeStateAtomic(state);
            this.ownsInstances = true;
        }
        finally {
            releaseLock();
        }
    }
    async registerDatabase(name, info) {
        if (!this.ownsInstances) {
            throw new Error("Cannot register database: not the owner");
        }
        const releaseLock = await this.acquireLock();
        try {
            const state = this.readState();
            state.databases[name] = info;
            this.writeStateAtomic(state);
        }
        finally {
            releaseLock();
        }
    }
    async registerService(name, info) {
        if (!this.ownsInstances) {
            throw new Error("Cannot register service: not the owner");
        }
        const releaseLock = await this.acquireLock();
        try {
            const state = this.readState();
            state.services[name] = info;
            this.writeStateAtomic(state);
        }
        finally {
            releaseLock();
        }
    }
    async releaseOwnership() {
        if (!this.ownsInstances) {
            return;
        }
        const releaseLock = await this.acquireLock();
        try {
            if (fs.existsSync(this.statePath)) {
                fs.unlinkSync(this.statePath);
            }
            this.ownsInstances = false;
        }
        finally {
            releaseLock();
        }
    }
    readState() {
        const content = fs.readFileSync(this.statePath, "utf-8");
        return JSON.parse(content);
    }
    writeStateAtomic(state) {
        this.ensureStateDir();
        const tmpPath = this.statePath + ".tmp";
        fs.writeFileSync(tmpPath, JSON.stringify(state, null, 2));
        fs.renameSync(tmpPath, this.statePath);
    }
}
//# sourceMappingURL=instance-state.js.map