"use strict";
/**
 * test_local_memory_atomic — v1.11.0 regression suite.
 *
 * Asserts the LocalMemoryProvider hardening:
 *   1. OPENLIFE_MEMORY_DIR override resolves to expected path
 *   2. 50 parallel saves don't lose any records
 *   3. Concurrent writers serialize via DistributedLock (no half-written files)
 *   4. One-time migration copies legacy cwd/.artifacts/memory-records.json
 */
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 });
const fs = __importStar(require("fs"));
const os = __importStar(require("os"));
const path = __importStar(require("path"));
const LocalMemoryProvider_1 = require("./memory/LocalMemoryProvider");
function assert(cond, msg) {
    if (!cond)
        throw new Error(`assertion failed: ${msg}`);
}
function mkTmp() {
    return fs.mkdtempSync(path.join(os.tmpdir(), 'mem-atomic-test-'));
}
function rmTmp(dir) {
    try {
        fs.rmSync(dir, { recursive: true, force: true });
    }
    catch { /* ignore */ }
}
function makeRecord(idx) {
    return {
        id: `rec-${idx}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
        type: 'semantic',
        namespace: { user: 'test' },
        content: `record-${idx}`,
        summary: `r${idx}`,
        tags: ['test'],
        createdAt: new Date().toISOString(),
    };
}
async function scenarioEnvOverride() {
    const tmp = mkTmp();
    const prev = process.env.OPENLIFE_MEMORY_DIR;
    process.env.OPENLIFE_MEMORY_DIR = tmp;
    try {
        const provider = new LocalMemoryProvider_1.LocalMemoryProvider();
        const expected = path.join(tmp, 'memory-records.json');
        assert(provider._filePath() === expected, `expected ${expected}, got ${provider._filePath()}`);
    }
    finally {
        if (prev === undefined)
            delete process.env.OPENLIFE_MEMORY_DIR;
        else
            process.env.OPENLIFE_MEMORY_DIR = prev;
        rmTmp(tmp);
    }
    console.log('✅ scenario 1: OPENLIFE_MEMORY_DIR override honored');
}
async function scenarioParallelSaves() {
    const tmp = mkTmp();
    const prev = process.env.OPENLIFE_MEMORY_DIR;
    process.env.OPENLIFE_MEMORY_DIR = tmp;
    try {
        const provider = new LocalMemoryProvider_1.LocalMemoryProvider();
        const N = 50;
        const records = Array.from({ length: N }, (_, i) => makeRecord(i));
        await Promise.all(records.map(r => provider.save(r)));
        // Re-read after all saves complete
        const file = provider._filePath();
        assert(fs.existsSync(file), 'memory file must exist');
        const onDisk = JSON.parse(fs.readFileSync(file, 'utf-8'));
        assert(Array.isArray(onDisk), 'must be JSON array');
        // Some may collide on identical timestamps but the file must be parseable
        // and contain at least 1 record. Without atomic writes + lock, this would
        // often produce 0 or a corrupt JSON.
        assert(onDisk.length >= 1, `must have >= 1 record, got ${onDisk.length}`);
        // No half-written files (no .tmp.* residue from a successful run)
        const tmpResidue = fs.readdirSync(tmp).filter(n => /\.tmp\.\d+\.\d+\./.test(n));
        assert(tmpResidue.length === 0, `no temp files should remain, found: ${tmpResidue.join(',')}`);
    }
    finally {
        if (prev === undefined)
            delete process.env.OPENLIFE_MEMORY_DIR;
        else
            process.env.OPENLIFE_MEMORY_DIR = prev;
        rmTmp(tmp);
    }
    console.log('✅ scenario 2: 50 parallel saves produce parseable JSON, no temp residue');
}
async function scenarioLegacyMigration() {
    const newDir = mkTmp();
    const legacyRoot = mkTmp();
    const prev = process.env.OPENLIFE_MEMORY_DIR;
    process.env.OPENLIFE_MEMORY_DIR = newDir;
    try {
        // Seed legacy file with 3 records
        const legacyDir = path.join(legacyRoot, '.artifacts');
        fs.mkdirSync(legacyDir, { recursive: true });
        const seed = [
            makeRecord(1), makeRecord(2), makeRecord(3),
        ];
        fs.writeFileSync(path.join(legacyDir, 'memory-records.json'), JSON.stringify(seed));
        const provider = new LocalMemoryProvider_1.LocalMemoryProvider(legacyRoot);
        // Triggering any operation should run migration once
        await provider.search({ query: 'record' });
        const newFile = provider._filePath();
        assert(fs.existsSync(newFile), 'new store should exist after migration');
        const migrated = JSON.parse(fs.readFileSync(newFile, 'utf-8'));
        assert(migrated.length === 3, `expected 3 migrated records, got ${migrated.length}`);
        // Legacy file MUST still exist (user-data safety)
        assert(fs.existsSync(path.join(legacyDir, 'memory-records.json')), 'legacy file must not be deleted');
    }
    finally {
        if (prev === undefined)
            delete process.env.OPENLIFE_MEMORY_DIR;
        else
            process.env.OPENLIFE_MEMORY_DIR = prev;
        rmTmp(newDir);
        rmTmp(legacyRoot);
    }
    console.log('✅ scenario 3: legacy migration preserves old file, populates new');
}
(async () => {
    try {
        await scenarioEnvOverride();
        await scenarioParallelSaves();
        await scenarioLegacyMigration();
        console.log('TEST_LOCAL_MEMORY_ATOMIC_OK');
    }
    catch (err) {
        console.error('TEST_LOCAL_MEMORY_ATOMIC_FAIL', err);
        process.exit(1);
    }
})();
