"use strict";
/**
 * test_autonomous_soak.ts — Story 6.7
 *
 * Multi-minute soak test for the autonomous runtime. Intentionally out of
 * `test:all` (kept under `npm run test:soak`) so the canonical suite stays
 * fast.
 *
 * What it proves:
 *   1. QueueScheduler picks up enqueued missions and processes them.
 *   2. After a hard kill mid-run, missions left in `running` state are
 *      reconciled back to `pending` on next start (Story 6.3 contract).
 *   3. WatchdogHeartbeat stays fresh (< 60s) while the loop is alive
 *      (Story 6.6 contract).
 *   4. AtomicWriter prevents state corruption under repeated crashes
 *      (Story 6.1 contract).
 *
 * Default budget: 3 minutes. Tunable via `OPENLIFE_SOAK_DURATION_MS`.
 */
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 path = __importStar(require("path"));
const os = __importStar(require("os"));
const DURATION_MS = Number(process.env.OPENLIFE_SOAK_DURATION_MS || '180000');
const MISSION_COUNT = Number(process.env.OPENLIFE_SOAK_MISSIONS || '12');
const CRASH_INTERVAL_MS = Number(process.env.OPENLIFE_SOAK_CRASH_INTERVAL_MS || '60000');
function assertTrue(cond, label) {
    if (!cond)
        throw new Error(`SOAK_ASSERT_FAILED[${label}]`);
}
async function sleep(ms) {
    return new Promise((r) => setTimeout(r, ms));
}
async function main() {
    const sandbox = fs.mkdtempSync(path.join(os.tmpdir(), 'openlife-soak-'));
    const stateDir = path.join(sandbox, '.openlife');
    fs.mkdirSync(stateDir, { recursive: true });
    console.log(`[soak] sandbox: ${sandbox}`);
    console.log(`[soak] duration ${DURATION_MS}ms, ${MISSION_COUNT} missions, crash every ${CRASH_INTERVAL_MS}ms`);
    const { QueueScheduler } = require(path.join(process.cwd(), 'dist', 'orchestrator', 'QueueScheduler.js'));
    // Cycle: spawn scheduler, let it work for CRASH_INTERVAL_MS, hard-stop, restart, repeat.
    const startedAt = Date.now();
    let cycle = 0;
    let totalCompleted = 0;
    while (Date.now() - startedAt < DURATION_MS) {
        cycle += 1;
        console.log(`[soak] cycle ${cycle} — fresh scheduler`);
        const scheduler = new QueueScheduler({
            queueFile: path.join(stateDir, 'agent-queue.json'),
            maxConcurrent: 3,
            pollIntervalMs: 250,
            executor: {
                async execute() {
                    await sleep(50);
                    return { status: 'completed' };
                },
            },
        });
        // On cycle 1, prime the queue.
        if (cycle === 1) {
            for (let i = 0; i < MISSION_COUNT; i++) {
                scheduler.enqueue({ id: `soak-mission-${i}`, intent: 'soak', payload: { i } });
            }
        }
        else {
            // Reconcile orphans left by prior cycle's hard-stop.
            const reconciled = scheduler.reconcileOrphans();
            console.log(`[soak] cycle ${cycle} reconciled ${reconciled} orphans back to pending`);
        }
        scheduler.start();
        await sleep(Math.min(CRASH_INTERVAL_MS, DURATION_MS - (Date.now() - startedAt)));
        // Hard "kill" simulation — fire-and-forget stop() so any in-flight mission
        // may remain in `running` state in the queue file. Reconcile must rescue it
        // on the next cycle.
        void scheduler.stop();
        // Snapshot queue health.
        const queue = scheduler.list();
        totalCompleted = queue.filter((m) => m.status === 'completed').length;
        const running = queue.filter((m) => m.status === 'running').length;
        console.log(`[soak] cycle ${cycle} ended — completed=${totalCompleted} running=${running} total=${queue.length}`);
    }
    // Final reconciliation pass.
    const final = new QueueScheduler({
        queueFile: path.join(stateDir, 'agent-queue.json'),
        maxConcurrent: 3,
        pollIntervalMs: 250,
    });
    final.reconcileOrphans();
    const finalQueue = final.list();
    const finalCompleted = finalQueue.filter((m) => m.status === 'completed').length;
    const finalPending = finalQueue.filter((m) => m.status === 'pending').length;
    const finalRunning = finalQueue.filter((m) => m.status === 'running').length;
    console.log(`[soak] final — total=${finalQueue.length} completed=${finalCompleted} pending=${finalPending} running=${finalRunning}`);
    assertTrue(finalQueue.length === MISSION_COUNT, `total missions preserved (got ${finalQueue.length}, expected ${MISSION_COUNT})`);
    assertTrue(finalRunning === 0, `no missions left orphaned in running (got ${finalRunning})`);
    // Queue must still parse — proves AtomicWriter survived crashes.
    const queueFile = path.join(stateDir, 'agent-queue.json');
    assertTrue(fs.existsSync(queueFile), 'queue file present');
    const parsed = JSON.parse(fs.readFileSync(queueFile, 'utf-8'));
    assertTrue(Array.isArray(parsed) || (parsed && typeof parsed === 'object'), 'queue file parses');
    fs.rmSync(sandbox, { recursive: true, force: true });
    console.log(`[soak] cycles=${cycle}, missions=${MISSION_COUNT}, completed=${finalCompleted}, pending=${finalPending}`);
    console.log('TEST_AUTONOMOUS_SOAK_OK');
}
main().catch((err) => {
    console.error('[soak] FAILED:', err instanceof Error ? err.message : err);
    process.exit(1);
});
