/**
 * Audit Chain Service
 *
 * Provides immutable, hash-chained audit records for compliance.
 * Supports cryptographic signing and tamper detection.
 *
 * @packageDocumentation
 */
import { createHash, createSign, createVerify, randomUUID } from 'crypto';
import { createLogger } from '../common/logger.js';
const logger = createLogger({ component: 'audit-chain' });
export class AuditChainService {
    storage;
    privateKey;
    publicKey;
    signRecords;
    sequenceCache = new Map();
    constructor(config) {
        this.storage = config.storage;
        this.privateKey = config.privateKey;
        this.publicKey = config.publicKey;
        this.signRecords = config.signRecords ?? false;
        if (this.signRecords && !this.privateKey) {
            throw new Error('Private key required for signing');
        }
        logger.info({ signRecords: this.signRecords }, 'Audit chain service initialized');
    }
    // ==========================================================================
    // Record Creation
    // ==========================================================================
    /**
     * Append a new audit record to the chain
     */
    async append(tenantId, entityType, entityId, action, actor, payload) {
        // Get previous record for chaining
        const previous = await this.storage.getLatest(tenantId);
        const previousHash = previous?.dataHash ?? 'genesis';
        const sequence = (previous?.sequence ?? 0) + 1;
        // Compute data hash
        const dataHash = this.computeHash(payload);
        // Create record
        const record = {
            id: randomUUID(),
            tenantId,
            entityType,
            entityId,
            action,
            actor,
            previousHash,
            dataHash,
            payload,
            timestamp: new Date().toISOString(),
            sequence,
        };
        // Sign if enabled
        if (this.signRecords && this.privateKey) {
            record.signature = this.signRecord(record);
        }
        // Persist
        await this.storage.append(record);
        // Update sequence cache
        this.sequenceCache.set(tenantId, sequence);
        logger.debug({ tenantId, entityType, entityId, action, sequence }, 'Audit record appended');
        return record;
    }
    /**
     * Append multiple records atomically
     */
    async appendBatch(records) {
        const results = [];
        for (const input of records) {
            const record = await this.append(input.tenantId, input.entityType, input.entityId, input.action, input.actor, input.payload);
            results.push(record);
        }
        return results;
    }
    // ==========================================================================
    // Chain Verification
    // ==========================================================================
    /**
     * Verify the integrity of the audit chain
     */
    async verifyChain(tenantId, startSequence, endSequence) {
        const start = startSequence ?? 1;
        const stats = await this.storage.getStats(tenantId);
        const end = endSequence ?? stats.headSequence;
        const records = await this.storage.getBySequenceRange(tenantId, start, end);
        if (records.length === 0) {
            return { valid: true, recordCount: 0 };
        }
        let previousHash = start === 1 ? 'genesis' : records[0].previousHash;
        for (let i = 0; i < records.length; i++) {
            const record = records[i];
            // Verify chain link
            if (record.previousHash !== previousHash) {
                return {
                    valid: false,
                    recordCount: i,
                    brokenAt: record.sequence,
                    error: `Chain broken at sequence ${record.sequence}: expected previous hash ${previousHash}, got ${record.previousHash}`,
                };
            }
            // Verify data hash
            const computedHash = this.computeHash(record.payload);
            if (record.dataHash !== computedHash) {
                return {
                    valid: false,
                    recordCount: i,
                    brokenAt: record.sequence,
                    error: `Data tampered at sequence ${record.sequence}: hash mismatch`,
                };
            }
            // Verify signature if present
            if (record.signature && this.publicKey) {
                const signatureValid = this.verifySignature(record);
                if (!signatureValid) {
                    return {
                        valid: false,
                        recordCount: i,
                        brokenAt: record.sequence,
                        error: `Invalid signature at sequence ${record.sequence}`,
                    };
                }
            }
            previousHash = record.dataHash;
        }
        return { valid: true, recordCount: records.length };
    }
    /**
     * Verify a single record's integrity
     */
    verifyRecord(record) {
        // Verify data hash
        const computedHash = this.computeHash(record.payload);
        if (record.dataHash !== computedHash) {
            return false;
        }
        // Verify signature if present
        if (record.signature && this.publicKey) {
            return this.verifySignature(record);
        }
        return true;
    }
    // ==========================================================================
    // Querying
    // ==========================================================================
    /**
     * Query audit records
     */
    async query(query) {
        return this.storage.query(query);
    }
    /**
     * Get audit history for an entity
     */
    async getEntityHistory(tenantId, entityType, entityId, limit) {
        return this.storage.query({
            tenantId,
            entityType,
            entityId,
            limit: limit ?? 100,
        });
    }
    /**
     * Get recent activity for a tenant
     */
    async getRecentActivity(tenantId, limit = 50) {
        return this.storage.query({ tenantId, limit });
    }
    /**
     * Get chain statistics
     */
    async getStats(tenantId) {
        return this.storage.getStats(tenantId);
    }
    // ==========================================================================
    // Cryptographic Helpers
    // ==========================================================================
    /**
     * Compute SHA-256 hash of payload
     */
    computeHash(payload) {
        const data = JSON.stringify(payload, Object.keys(payload).sort());
        return createHash('sha256').update(data).digest('hex');
    }
    /**
     * Sign a record using Ed25519
     */
    signRecord(record) {
        if (!this.privateKey) {
            throw new Error('Private key not configured');
        }
        const data = this.getSignableData(record);
        const sign = createSign('SHA256');
        sign.update(data);
        return sign.sign(this.privateKey, 'base64');
    }
    /**
     * Verify record signature
     */
    verifySignature(record) {
        if (!this.publicKey || !record.signature) {
            return false;
        }
        const data = this.getSignableData(record);
        const verify = createVerify('SHA256');
        verify.update(data);
        return verify.verify(this.publicKey, record.signature, 'base64');
    }
    /**
     * Get data to sign (deterministic ordering)
     */
    getSignableData(record) {
        return JSON.stringify({
            id: record.id,
            tenantId: record.tenantId,
            entityType: record.entityType,
            entityId: record.entityId,
            action: record.action,
            actor: record.actor,
            previousHash: record.previousHash,
            dataHash: record.dataHash,
            timestamp: record.timestamp,
            sequence: record.sequence,
        });
    }
}
// ============================================================================
// In-Memory Storage (for testing)
// ============================================================================
export class InMemoryAuditStorage {
    records = new Map();
    async append(record) {
        const tenantRecords = this.records.get(record.tenantId) ?? [];
        tenantRecords.push(record);
        this.records.set(record.tenantId, tenantRecords);
    }
    async getLatest(tenantId) {
        const tenantRecords = this.records.get(tenantId);
        if (!tenantRecords || tenantRecords.length === 0) {
            return null;
        }
        return tenantRecords[tenantRecords.length - 1];
    }
    async query(query) {
        let results = [];
        if (query.tenantId) {
            results = this.records.get(query.tenantId) ?? [];
        }
        else {
            for (const tenantRecords of this.records.values()) {
                results.push(...tenantRecords);
            }
        }
        // Apply filters
        if (query.entityType) {
            results = results.filter((r) => r.entityType === query.entityType);
        }
        if (query.entityId) {
            results = results.filter((r) => r.entityId === query.entityId);
        }
        if (query.action) {
            results = results.filter((r) => r.action === query.action);
        }
        if (query.actor) {
            results = results.filter((r) => r.actor === query.actor);
        }
        if (query.startTime) {
            results = results.filter((r) => r.timestamp >= query.startTime);
        }
        if (query.endTime) {
            results = results.filter((r) => r.timestamp <= query.endTime);
        }
        // Sort by sequence descending (most recent first)
        results.sort((a, b) => b.sequence - a.sequence);
        // Apply pagination
        if (query.offset) {
            results = results.slice(query.offset);
        }
        if (query.limit) {
            results = results.slice(0, query.limit);
        }
        return results;
    }
    async getBySequenceRange(tenantId, start, end) {
        const tenantRecords = this.records.get(tenantId) ?? [];
        return tenantRecords
            .filter((r) => r.sequence >= start && r.sequence <= end)
            .sort((a, b) => a.sequence - b.sequence);
    }
    async getStats(tenantId) {
        const tenantRecords = this.records.get(tenantId) ?? [];
        const byEntityType = {
            agent: 0,
            attestation: 0,
            trust: 0,
            a2a: 0,
            sandbox: 0,
            config: 0,
        };
        const byAction = {
            create: 0,
            update: 0,
            delete: 0,
            activate: 0,
            suspend: 0,
            revoke: 0,
            tier_change: 0,
            approval: 0,
            invoke: 0,
            complete: 0,
            violation: 0,
        };
        for (const record of tenantRecords) {
            byEntityType[record.entityType]++;
            byAction[record.action]++;
        }
        const head = tenantRecords[tenantRecords.length - 1];
        return {
            totalRecords: tenantRecords.length,
            byEntityType,
            byAction,
            headHash: head?.dataHash ?? 'genesis',
            headSequence: head?.sequence ?? 0,
        };
    }
}
// ============================================================================
// Factory Functions
// ============================================================================
let instance = null;
export function createAuditChainService(config) {
    instance = new AuditChainService(config);
    return instance;
}
export function getAuditChainService() {
    if (!instance) {
        throw new Error('AuditChainService not initialized');
    }
    return instance;
}
//# sourceMappingURL=audit.js.map