"use strict";
/**
 * StateManager -- Centralized governance state coordination.
 *
 * Provides a single source of truth for governance state across
 * all modules. Prevents duplicate initialization, manages module
 * lifecycle, and coordinates shared resources.
 *
 * T-024: Shared state manager to prevent fragmented governance state.
 *
 * @connexum/ai-governance
 */
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.StateManager = void 0;
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const crypto = __importStar(require("crypto"));
const log_1 = require("../util/log");
const log = (0, log_1.createLogger)('state-manager');
class StateManager {
    config;
    modules = new Map();
    version = 0;
    stateFile;
    lockFile;
    saveTimer = null;
    dirty = false;
    constructor(config) {
        this.config = {
            useLocking: true,
            saveIntervalMs: 30000,
            ...config,
        };
        if (!fs.existsSync(config.stateDir)) {
            fs.mkdirSync(config.stateDir, { recursive: true });
        }
        this.stateFile = path.join(config.stateDir, `state-${config.agentId}.json`);
        this.lockFile = path.join(config.stateDir, `state-${config.agentId}.lock`);
        // Load existing state if available
        this.loadState();
    }
    /**
     * Register a governance module.
     */
    registerModule(name, initialData) {
        if (this.modules.has(name)) {
            log.warn('Module already registered', { name });
            return;
        }
        this.modules.set(name, {
            name,
            initialized: true,
            lastActivity: new Date().toISOString(),
            data: initialData || {},
        });
        this.dirty = true;
        this.version++;
        log.info('Module registered', { name });
    }
    /**
     * Update module state data.
     */
    updateModule(name, data) {
        const module = this.modules.get(name);
        if (!module) {
            throw new Error(`Module '${name}' not registered`);
        }
        module.data = { ...module.data, ...data };
        module.lastActivity = new Date().toISOString();
        this.dirty = true;
        this.version++;
    }
    /**
     * Get module state.
     */
    getModule(name) {
        return this.modules.get(name);
    }
    /**
     * Get data from a specific module.
     */
    getModuleData(name, key) {
        const module = this.modules.get(name);
        if (!module)
            return undefined;
        return module.data[key];
    }
    /**
     * Check if a module is registered and initialized.
     */
    isModuleReady(name) {
        const module = this.modules.get(name);
        return module?.initialized === true;
    }
    /**
     * Deregister a module (cleanup).
     */
    deregisterModule(name) {
        const existed = this.modules.delete(name);
        if (existed) {
            this.dirty = true;
            this.version++;
        }
        return existed;
    }
    /**
     * Get a full snapshot of governance state.
     */
    getSnapshot() {
        const modulesObj = {};
        for (const [name, state] of this.modules) {
            modulesObj[name] = { ...state };
        }
        const snapshot = {
            agentId: this.config.agentId,
            version: this.version,
            modules: modulesObj,
            createdAt: new Date().toISOString(),
            updatedAt: new Date().toISOString(),
            checksum: '',
        };
        snapshot.checksum = this.computeChecksum(snapshot);
        return snapshot;
    }
    /**
     * Verify snapshot integrity.
     */
    verifySnapshot(snapshot) {
        const expected = snapshot.checksum;
        const actual = this.computeChecksum({ ...snapshot, checksum: '' });
        return expected === actual;
    }
    /**
     * List all registered modules.
     */
    listModules() {
        return Array.from(this.modules.keys());
    }
    /**
     * Get module count.
     */
    getModuleCount() {
        return this.modules.size;
    }
    /**
     * Save state to disk.
     */
    save() {
        if (this.config.useLocking) {
            this.acquireLock();
        }
        try {
            const snapshot = this.getSnapshot();
            fs.writeFileSync(this.stateFile, JSON.stringify(snapshot, null, 2));
            this.dirty = false;
            log.info('State saved', { version: this.version, modules: this.modules.size });
        }
        finally {
            if (this.config.useLocking) {
                this.releaseLock();
            }
        }
    }
    /**
     * Start periodic auto-save.
     */
    startAutoSave() {
        if (this.saveTimer)
            return;
        this.saveTimer = setInterval(() => {
            if (this.dirty) {
                try {
                    this.save();
                }
                catch (err) {
                    log.error('Auto-save failed', { error: err.message });
                }
            }
        }, this.config.saveIntervalMs);
    }
    /**
     * Stop auto-save and do a final save.
     */
    stop() {
        if (this.saveTimer) {
            clearInterval(this.saveTimer);
            this.saveTimer = null;
        }
        if (this.dirty) {
            try {
                this.save();
            }
            catch { /* best effort */ }
        }
    }
    // --- Private ---
    loadState() {
        if (!fs.existsSync(this.stateFile))
            return;
        try {
            const raw = fs.readFileSync(this.stateFile, 'utf-8');
            const snapshot = JSON.parse(raw);
            if (!this.verifySnapshot(snapshot)) {
                log.error('State file integrity check failed -- starting fresh');
                return;
            }
            for (const [name, state] of Object.entries(snapshot.modules)) {
                this.modules.set(name, state);
            }
            this.version = snapshot.version;
            log.info('State loaded', { version: this.version, modules: this.modules.size });
        }
        catch (err) {
            log.error('Failed to load state', { error: err.message });
        }
    }
    computeChecksum(snapshot) {
        const data = JSON.stringify({
            agentId: snapshot.agentId,
            version: snapshot.version,
            modules: snapshot.modules,
        });
        return crypto.createHash('sha256').update(data).digest('hex');
    }
    acquireLock() {
        const maxWait = 5000;
        const start = Date.now();
        while (fs.existsSync(this.lockFile)) {
            if (Date.now() - start > maxWait) {
                // Stale lock -- force break
                log.warn('Breaking stale lock', { lockFile: this.lockFile });
                try {
                    fs.unlinkSync(this.lockFile);
                }
                catch { /* ignore */ }
                break;
            }
            // Sleep 50ms without busy-spinning (uses Atomics.wait on a dummy buffer)
            Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 50);
        }
        fs.writeFileSync(this.lockFile, `${process.pid}\n${new Date().toISOString()}`);
    }
    releaseLock() {
        try {
            if (fs.existsSync(this.lockFile)) {
                fs.unlinkSync(this.lockFile);
            }
        }
        catch { /* ignore */ }
    }
}
exports.StateManager = StateManager;
//# sourceMappingURL=state-manager.js.map