import { readFileSync, writeFileSync, renameSync, unlinkSync } from 'fs';
import { writeFile, rename } from 'fs/promises';
import { SESSION_STATE_FILE, CONFIG_DIR } from './config.js';
import { mkdirSync, existsSync } from 'fs';
function isEphemeral() {
    return !!process.env.AGENTLINK_NO_STATE;
}
export function loadPersistedState() {
    if (isEphemeral())
        return null;
    try {
        const raw = readFileSync(SESSION_STATE_FILE, 'utf-8');
        const data = JSON.parse(raw);
        if (data.version !== 1)
            return null;
        return data;
    }
    catch {
        return null;
    }
}
/** Synchronous save — used ONLY for SIGINT/SIGTERM shutdown handlers.
 *  Uses a distinct temp file suffix to avoid colliding with async saves. */
export function savePersistedState(state) {
    if (isEphemeral())
        return;
    if (!existsSync(CONFIG_DIR)) {
        mkdirSync(CONFIG_DIR, { recursive: true });
    }
    const tmp = SESSION_STATE_FILE + '.sync.tmp';
    const json = JSON.stringify(state, null, 2) + '\n';
    writeFileSync(tmp, json, { encoding: 'utf-8', mode: 0o600 });
    renameSync(tmp, SESSION_STATE_FILE);
}
/** Async save — used for debounced runtime persistence (non-blocking). */
export async function savePersistedStateAsync(state) {
    if (isEphemeral())
        return;
    if (!existsSync(CONFIG_DIR)) {
        mkdirSync(CONFIG_DIR, { recursive: true });
    }
    const tmp = SESSION_STATE_FILE + '.async.tmp';
    const json = JSON.stringify(state, null, 2) + '\n';
    await writeFile(tmp, json, { encoding: 'utf-8', mode: 0o600 });
    await rename(tmp, SESSION_STATE_FILE);
}
export function clearPersistedState() {
    if (isEphemeral())
        return;
    for (const f of [SESSION_STATE_FILE, SESSION_STATE_FILE + '.sync.tmp', SESSION_STATE_FILE + '.async.tmp']) {
        try {
            unlinkSync(f);
        }
        catch { /* file may not exist */ }
    }
}
// Debounced persistence manager
let dirty = false;
let debounceTimer = null;
let collectState = null;
let flushInFlight = null;
export function setStateCollector(fn) {
    collectState = fn;
}
export function markDirty() {
    if (isEphemeral() || !collectState)
        return;
    dirty = true;
    if (debounceTimer)
        clearTimeout(debounceTimer);
    debounceTimer = setTimeout(() => {
        void flushStateAsync().catch((err) => {
            console.error('[Persistence] Failed to flush state:', err.message);
        });
    }, 500);
}
/** Sync flush — for shutdown handlers only. */
export function flushState() {
    if (debounceTimer) {
        clearTimeout(debounceTimer);
        debounceTimer = null;
    }
    if (!dirty || !collectState)
        return;
    try {
        savePersistedState(collectState());
        dirty = false;
    }
    catch (err) {
        console.error('[Persistence] Failed to save state:', err.message);
    }
}
/** Async flush — for debounced runtime persistence (non-blocking).
 *  Serializes writes so concurrent calls don't overlap, and preserves
 *  the dirty flag if a mutation arrives during an in-flight save. */
export async function flushStateAsync() {
    if (debounceTimer) {
        clearTimeout(debounceTimer);
        debounceTimer = null;
    }
    if (!dirty || !collectState)
        return;
    // Serialize: wait for any in-flight save to finish first
    if (flushInFlight) {
        try {
            await flushInFlight;
        }
        catch {
            // Previous save failed — proceed with our own save attempt
        }
        // After waiting, re-check — the previous save may have cleared dirty
        if (!dirty || !collectState)
            return;
    }
    // Snapshot state and clear dirty BEFORE the await so that any markDirty()
    // during the write correctly re-sets the flag for a subsequent flush.
    const state = collectState();
    dirty = false;
    try {
        flushInFlight = savePersistedStateAsync(state);
        await flushInFlight;
    }
    catch (err) {
        // Restore dirty so the next flush retries
        dirty = true;
        console.error('[Persistence] Failed to save state:', err.message);
    }
    finally {
        flushInFlight = null;
    }
}
//# sourceMappingURL=state-persistence.js.map