// engine/audit-trail/verifier.ts — Validate SHA-256 hash chain integrity (SPEC-167)
import { sha256 } from './logger.js';
import { isNodeError, canonicalJson, readJsonlLines } from './utils.js';
/** Recompute the canonical hash for an event using the shared canonicalJson helper. */
function recomputeHash(event) {
    return sha256(canonicalJson(event));
}
/**
 * Verify that the SHA-256 hash chain of the JSONL log file at filePath is intact.
 * Validates:
 *   1. Each entry's `hash` matches the recomputed hash of its canonical JSON.
 *   2. Each entry's `previousHash` matches the previous entry's `hash`.
 *   3. The first entry has an empty `previousHash`.
 */
export async function verifyChainIntegrity(filePath) {
    let lines;
    try {
        lines = await readJsonlLines(filePath);
    }
    catch (err) {
        if (isNodeError(err) && err.code === 'ENOENT') {
            return {
                valid: true,
                totalEntries: 0,
                message: 'Audit log is empty — chain is valid.',
            };
        }
        throw err;
    }
    if (lines.length === 0) {
        return {
            valid: true,
            totalEntries: 0,
            message: 'Audit log is empty — chain is valid.',
        };
    }
    let previousHash = '';
    for (let i = 0; i < lines.length; i++) {
        let event;
        try {
            // Cast to Partial first to validate required fields before narrowing
            const parsed = JSON.parse(lines[i] ?? '');
            if (parsed.previousHash === undefined || parsed.hash === undefined) {
                return {
                    valid: false,
                    totalEntries: lines.length,
                    firstBrokenAt: i + 1,
                    message: `Line ${String(i + 1)} is missing required audit fields.`,
                };
            }
            event = parsed;
        }
        catch {
            return {
                valid: false,
                totalEntries: lines.length,
                firstBrokenAt: i + 1,
                message: `Line ${String(i + 1)} is not valid JSON.`,
            };
        }
        // Verify previousHash linkage
        if (event.previousHash !== previousHash) {
            return {
                valid: false,
                totalEntries: lines.length,
                firstBrokenAt: i + 1,
                message: `Chain broken at entry ${String(i + 1)}: previousHash mismatch.`,
            };
        }
        // Verify self-hash
        const expected = recomputeHash(event);
        if (event.hash !== expected) {
            return {
                valid: false,
                totalEntries: lines.length,
                firstBrokenAt: i + 1,
                message: `Entry ${String(i + 1)} hash mismatch — possible tampering detected.`,
            };
        }
        previousHash = event.hash;
    }
    return {
        valid: true,
        totalEntries: lines.length,
        message: `Chain verified — ${String(lines.length)} entries are intact.`,
    };
}
//# sourceMappingURL=verifier.js.map