/**
 * audit3-4 smoke — every worker setInterval has a re-entry guard.
 *
 * Run: npx tsx scripts/smoke/audit3-4-tick-guards.ts
 *
 * Why this exists (2026-05-28):
 *   scripts/worker.ts runs 8 ticks at different cadences. Before this
 *   change only the monitor + outbox ticks had re-entry guards; the
 *   other 6 (dedup-purge, rollup, report, heartbeat-sweep, audit-export,
 *   escalation) used bare setInterval. If any tick ever blocks longer
 *   than its cadence (slow DB, network blip), they stack up
 *   unbounded in Node's microtask queue.
 *
 *   This smoke is a regression detector — counts setInterval call
 *   sites in worker.ts, counts isRunning*Tick guard variables, asserts
 *   the numbers match and that the shutdown drain awaits all of them.
 */
import './_lib';
import { ok, fail } from './_lib';
import fs from 'fs';
import path from 'path';

const WORKER_SRC = path.resolve(__dirname, '../../scripts/worker.ts');

function main(): void {
    const src = fs.readFileSync(WORKER_SRC, 'utf8');

    const intervals = src.match(/setInterval\s*\(/g) || [];
    const intervalCount = intervals.length;

    if (intervalCount < 8) {
        fail(
            'audit3-4 interval count',
            `worker.ts has ${intervalCount} setInterval calls; expected ≥8 (monitor, outbox, dedup-purge, rollup, report, heartbeat-sweep, audit-export, escalation)`,
        );
    }
    ok(`worker.ts has ${intervalCount} setInterval call sites`);

    // Each tick should have an isRunning*Tick = false declaration.
    const guards = src.match(/let\s+isRunning\w+Tick\s*=\s*false/g) || [];
    if (guards.length < intervalCount) {
        fail(
            'audit3-4 guard count',
            `worker.ts has ${guards.length} isRunning*Tick guards for ${intervalCount} setInterval calls — every tick must declare a guard`,
        );
    }
    ok(`worker.ts declares ${guards.length} isRunning*Tick guard variables`);

    // Each setInterval body should have an `if (isRunning…Tick) ... return;`
    // at the top. Match both the short form `if (x) return;` and the
    // long form `if (x) { console.log(...); return; }`.
    const earlyReturns = src.match(/if\s*\(\s*isRunning\w+Tick\s*\)(?:\s*return\s*;|\s*\{[^}]*return\s*;[^}]*\})/g) || [];
    if (earlyReturns.length < intervalCount) {
        fail(
            'audit3-4 early-return guards',
            `expected ${intervalCount} "if (isRunning*Tick) ... return;" guards, found ${earlyReturns.length}`,
        );
    }
    ok(`each tick has an "if (isRunning*Tick) ... return;" guard`);

    // Each setInterval body should have a finally { isRunning…Tick = false; }.
    const finallyResets = src.match(/isRunning\w+Tick\s*=\s*false/g) || [];
    // The 8 declarations also match this; we need 8 declarations + 8 resets in finally = 16.
    if (finallyResets.length < intervalCount * 2) {
        fail(
            'audit3-4 finally resets',
            `expected ≥${intervalCount * 2} "isRunning*Tick = false" assignments (one declaration + one finally per tick), found ${finallyResets.length}`,
        );
    }
    ok(`each tick has a "finally { isRunning*Tick = false }"`);

    // Shutdown drain must wait for ALL guards. We look for `anyInflight`
    // or a long boolean chain in the shutdown function.
    if (!/anyInflight\s*\(\s*\)|isRunningEscalationTick\s*\|\|.*isRunningRollupTick/s.test(src)) {
        fail(
            'audit3-4 drain coverage',
            'shutdown drain does not await the new ticks — graceful shutdown could leave inflight work',
        );
    }
    ok('shutdown drain waits for every tick');

    ok('audit3-4 worker tick guards', `${intervalCount} ticks, ${guards.length} declared guards, all drained on shutdown`);
}

main();
