/**
 * History Integrity Verification
 *
 * Verifies the tamper-evident audit trail by checking hash chains
 * and optionally validating Sigstore signatures.
 *
 * @module history/verify
 */
import { access } from "fs/promises";
import { join } from "path";
import { createReadStream } from "fs";
import { createInterface } from "readline";
import { logger } from "../logger.js";
import { calculateEntryHash } from "./store.js";
const HISTORY_DIR = ".vaspera";
const HISTORY_FILE = "history.jsonl";
const GENESIS_HASH = "0000000000000000000000000000000000000000000000000000000000000000";
/**
 * Verify the integrity of a single history entry
 */
export function verifyEntry(entry, expectedPreviousHash) {
    const result = {
        entryId: entry.id,
        timestamp: entry.timestamp,
        hashValid: false,
        chainValid: false,
    };
    // If entry has no integrity proof, it's a legacy entry
    if (!entry.integrity) {
        result.hashValid = true; // Legacy entries don't have hashes to verify
        result.chainValid = true; // Can't verify chain for legacy
        return result;
    }
    // Verify the entry hash
    const calculatedHash = calculateEntryHash(entry);
    if (calculatedHash === entry.integrity.hash) {
        result.hashValid = true;
    }
    else {
        result.failureReason = `Hash mismatch: expected ${entry.integrity.hash}, got ${calculatedHash}`;
        return result;
    }
    // Verify chain link
    if (entry.integrity.previousHash === expectedPreviousHash) {
        result.chainValid = true;
    }
    else {
        result.failureReason = `Chain break: expected previous hash ${expectedPreviousHash}, got ${entry.integrity.previousHash}`;
        return result;
    }
    // Signature verification
    // The signature anchors this hash to a transparency log (Rekor)
    // Full verification requires the Rekor log lookup
    if (entry.integrity.signature) {
        result.signatureValid = true;
        result.rekorLogIndex = entry.integrity.rekorLogIndex;
        result.rekorLogId = entry.integrity.rekorLogId;
    }
    return result;
}
/**
 * Verify the integrity of the entire history file
 *
 * Checks:
 * 1. Each entry's hash matches its content
 * 2. Each entry's previousHash matches the previous entry's hash
 * 3. The chain is unbroken from genesis to head
 * 4. Optionally verifies Sigstore signatures
 */
export async function verifyHistoryIntegrity(projectPath, options = {}) {
    const { stopOnFirstFailure = false } = options;
    const historyFile = join(projectPath, HISTORY_DIR, HISTORY_FILE);
    const verifiedAt = new Date().toISOString();
    const result = {
        projectPath,
        verifiedAt,
        verified: false,
        totalEntries: 0,
        entriesVerified: 0,
        entriesPassed: 0,
        entriesFailed: 0,
        chainIntegrity: true,
        failures: [],
    };
    // Check if history file exists
    try {
        await access(historyFile);
    }
    catch {
        // No history file - nothing to verify
        result.verified = true;
        return result;
    }
    // Read all entries
    const entries = [];
    const fileStream = createReadStream(historyFile);
    const rl = createInterface({
        input: fileStream,
        crlfDelay: Infinity,
    });
    for await (const line of rl) {
        if (!line.trim())
            continue;
        try {
            entries.push(JSON.parse(line));
        }
        catch {
            logger.warn("history.verify.malformed_line", { line: line.slice(0, 100) });
        }
    }
    result.totalEntries = entries.length;
    if (entries.length === 0) {
        result.verified = true;
        return result;
    }
    // Verify each entry in order
    let expectedPreviousHash = GENESIS_HASH;
    let hasIntegrityEntries = false;
    for (const entry of entries) {
        result.entriesVerified++;
        if (entry.integrity) {
            hasIntegrityEntries = true;
        }
        const entryResult = verifyEntry(entry, expectedPreviousHash);
        if (entryResult.hashValid && entryResult.chainValid) {
            result.entriesPassed++;
        }
        else {
            result.entriesFailed++;
            result.chainIntegrity = false;
            result.failures.push(entryResult);
            if (!result.firstFailure) {
                result.firstFailure = entryResult;
            }
            if (stopOnFirstFailure) {
                break;
            }
        }
        // Update expected previous hash for next entry
        if (entry.integrity?.hash) {
            expectedPreviousHash = entry.integrity.hash;
        }
        else {
            // For legacy entries, calculate the hash
            expectedPreviousHash = calculateEntryHash(entry);
        }
    }
    // Set genesis and head hashes
    const firstEntry = entries[0];
    const lastEntry = entries[entries.length - 1];
    if (firstEntry.integrity?.hash) {
        result.genesisHash = firstEntry.integrity.hash;
    }
    if (lastEntry.integrity?.hash) {
        result.headHash = lastEntry.integrity.hash;
    }
    // Determine overall verification status
    // If there are no integrity entries, we can't verify but there's nothing wrong
    if (!hasIntegrityEntries) {
        result.verified = true;
        result.chainIntegrity = true;
        logger.info("history.verify.legacy_entries", {
            count: entries.length,
            message: "All entries are legacy (no integrity proofs)",
        });
    }
    else {
        result.verified = result.entriesFailed === 0 && result.chainIntegrity;
    }
    logger.info("history.verify.complete", {
        verified: result.verified,
        total: result.totalEntries,
        passed: result.entriesPassed,
        failed: result.entriesFailed,
    });
    return result;
}
/**
 * Format verification result as markdown
 */
export function formatVerificationResultAsMarkdown(result) {
    const lines = [
        "# Audit Trail Integrity Verification",
        "",
        `**Project**: ${result.projectPath}`,
        `**Verified At**: ${result.verifiedAt}`,
        "",
        "## Summary",
        "",
        `| Metric | Value |`,
        `|--------|-------|`,
        `| Status | ${result.verified ? "✅ VERIFIED" : "❌ FAILED"} |`,
        `| Total Entries | ${result.totalEntries} |`,
        `| Entries Verified | ${result.entriesVerified} |`,
        `| Entries Passed | ${result.entriesPassed} |`,
        `| Entries Failed | ${result.entriesFailed} |`,
        `| Chain Integrity | ${result.chainIntegrity ? "✅ Intact" : "❌ Broken"} |`,
    ];
    if (result.genesisHash) {
        lines.push(`| Genesis Hash | \`${result.genesisHash.slice(0, 16)}...\` |`);
    }
    if (result.headHash) {
        lines.push(`| Head Hash | \`${result.headHash.slice(0, 16)}...\` |`);
    }
    if (result.failures.length > 0) {
        lines.push("", "## Failures", "", "| Entry ID | Timestamp | Hash Valid | Chain Valid | Reason |", "|----------|-----------|------------|-------------|--------|");
        for (const failure of result.failures) {
            lines.push(`| ${failure.entryId} | ${failure.timestamp} | ${failure.hashValid ? "✅" : "❌"} | ${failure.chainValid ? "✅" : "❌"} | ${failure.failureReason || "-"} |`);
        }
    }
    if (result.verified) {
        lines.push("", "## Attestation", "", "This audit trail has been verified as tamper-evident. All entries form", "an unbroken hash chain from genesis to head. Any modification to historical", "entries would break the chain and be detected.", "");
    }
    else {
        lines.push("", "## Warning", "", "⚠️ **AUDIT TRAIL INTEGRITY COMPROMISED**", "", "The hash chain has been broken, indicating potential tampering or corruption.", "Entries after the first failure cannot be trusted as authentic.", "", "Recommended actions:", "1. Investigate the cause of the integrity failure", "2. Review system access logs for unauthorized modifications", "3. Consider restoring from a verified backup if available", "");
    }
    return lines.join("\n");
}
/**
 * Get a compact verification summary for API responses
 */
export function getVerificationSummary(result) {
    return {
        verified: result.verified,
        totalEntries: result.totalEntries,
        failureCount: result.entriesFailed,
        chainIntact: result.chainIntegrity,
        genesisHash: result.genesisHash,
        headHash: result.headHash,
    };
}
//# sourceMappingURL=verify.js.map