/**
 * Centralized Data Safety Manager
 *
 * SINGLE POINT OF SAFETY for ALL CLEO data operations.
 *
 * Design Principles:
 * - All data operations MUST flow through this layer
 * - Zero-config safety: works automatically for all callers
 * - Atomic operations: verify + checkpoint + log in single transaction
 * - Recoverable: every operation leaves system in valid state
 *
 * This eliminates the need for wrapper functions and ensures safety
 * at the architectural level, not as an afterthought.
 *
 * @task T4739
 * @epic T4732
 */
import { getLogger } from '../logger.js';
import { checkSequence, repairSequence } from '../sequence/index.js';
import { gitCheckpoint } from './git-checkpoint.js';
import { vacuumIntoBackup } from './sqlite-backup.js';
const log = getLogger('data-safety');
/** Safety violation error */
export class DataSafetyError extends Error {
    code;
    context;
    constructor(message, code, context) {
        super(message);
        this.code = code;
        this.context = context;
        this.name = 'DataSafetyError';
    }
}
const DEFAULT_SAFETY = {
    verify: true,
    checkpoint: true,
    validateSequence: true,
    strict: true,
};
const stats = {
    writes: 0,
    verifications: 0,
    checkpoints: 0,
    errors: 0,
    lastCheckpoint: null,
};
/** Get current safety statistics */
export function getSafetyStats() {
    return { ...stats };
}
/** Reset safety statistics (for testing) */
export function resetSafetyStats() {
    stats.writes = 0;
    stats.verifications = 0;
    stats.checkpoints = 0;
    stats.errors = 0;
    stats.lastCheckpoint = null;
}
/**
 * Verify sequence integrity before write.
 * Auto-repairs if sequence is behind database.
 */
async function ensureSequenceValid(cwd, options) {
    if (!options?.validateSequence)
        return;
    const check = await checkSequence(cwd);
    if (!check.valid) {
        log.warn({ counter: check.counter, maxId: check.maxIdInData }, 'Sequence behind, repairing');
        const repair = await repairSequence(cwd);
        if (!repair.repaired && options.strict) {
            throw new DataSafetyError(`Sequence repair failed: ${repair.message}`, 'SEQUENCE_INVALID', {
                check,
                repair,
            });
        }
    }
}
/**
 * Create a checkpoint after successful write.
 * Non-blocking - failures are logged but don't fail the operation.
 */
async function checkpoint(context, cwd, options) {
    if (!options?.checkpoint)
        return;
    try {
        await gitCheckpoint('auto', context, cwd);
        stats.checkpoints++;
        stats.lastCheckpoint = new Date();
    }
    catch (err) {
        // Checkpoint failures are non-fatal but logged
        log.warn({ err }, 'Checkpoint failed (non-fatal)');
    }
    vacuumIntoBackup({ cwd }).catch(() => { }); // non-fatal SQLite snapshot
}
/**
 * Verify sessions were written correctly.
 */
async function verifySessions(data, accessor, options) {
    if (!options?.verify)
        return;
    stats.verifications++;
    const readBack = await accessor.loadSessions();
    if (readBack.length !== data.length) {
        throw new DataSafetyError(`Sessions verification failed: count mismatch. Expected ${data.length}, got ${readBack.length}`, 'VERIFICATION_FAILED', { expected: data.length, actual: readBack.length });
    }
}
/**
 * Verify ArchiveFile was written correctly.
 */
async function verifyArchiveFile(data, accessor, options) {
    if (!options?.verify)
        return;
    stats.verifications++;
    const readBack = await accessor.loadArchive();
    if (!readBack) {
        throw new DataSafetyError('ArchiveFile verification failed: file not found after write', 'VERIFICATION_FAILED');
    }
    if (readBack.archivedTasks.length !== data.archivedTasks.length) {
        throw new DataSafetyError(`ArchiveFile verification failed: count mismatch. Expected ${data.archivedTasks.length}, got ${readBack.archivedTasks.length}`, 'VERIFICATION_FAILED', { expected: data.archivedTasks.length, actual: readBack.archivedTasks.length });
    }
}
/**
 * Safe wrapper for DataAccessor.saveSessions()
 */
export async function safeSaveSessions(accessor, data, cwd, options) {
    const opts = { ...DEFAULT_SAFETY, ...options };
    await accessor.saveSessions(data);
    stats.writes++;
    await verifySessions(data, accessor, opts);
    await checkpoint(`saved Sessions (${data.length} sessions)`, cwd, opts);
}
/**
 * Safe wrapper for DataAccessor.saveArchive()
 */
export async function safeSaveArchive(accessor, data, cwd, options) {
    const opts = { ...DEFAULT_SAFETY, ...options };
    await accessor.saveArchive(data);
    stats.writes++;
    await verifyArchiveFile(data, accessor, opts);
    await checkpoint(`saved Archive (${data.archivedTasks.length} tasks)`, cwd, opts);
}
/**
 * Safe wrapper for single-task write operations (T5034).
 *
 * Performs:
 * 1. Sequence validation
 * 2. Write operation (caller-provided function)
 * 3. Git checkpoint
 *
 * Verification is lightweight — no full-file read-back. The write
 * itself is a targeted SQL operation that either succeeds or throws.
 */
export async function safeSingleTaskWrite(_accessor, taskId, writeFn, cwd, options) {
    const opts = { ...DEFAULT_SAFETY, ...options };
    // 1. Validate sequence
    await ensureSequenceValid(cwd, opts);
    // 2. Perform targeted write
    await writeFn();
    stats.writes++;
    // 3. Checkpoint (lightweight — no full-file verify)
    await checkpoint(`single-task ${taskId}`, cwd, opts);
}
/**
 * Safe wrapper for DataAccessor.appendLog()
 *
 * Note: Log appends are fire-and-forget (no verification)
 * but we still checkpoint to ensure data is committed.
 */
export async function safeAppendLog(accessor, entry, cwd, options) {
    const opts = { ...DEFAULT_SAFETY, ...options, verify: false }; // Logs don't need verification
    await accessor.appendLog(entry);
    stats.writes++;
    await checkpoint('log entry', cwd, opts);
}
/**
 * Run comprehensive data integrity check.
 * Validates all data files and sequence consistency.
 */
export async function runDataIntegrityCheck(accessor, cwd) {
    const errors = [];
    const warnings = [];
    // 1. Check sequence
    try {
        const seqCheck = await checkSequence(cwd);
        if (!seqCheck.valid) {
            errors.push(`Sequence invalid: counter=${seqCheck.counter}, maxId=T${seqCheck.maxIdInData}`);
            // Try to repair
            const repair = await repairSequence(cwd);
            if (repair.repaired) {
                warnings.push(`Auto-repaired sequence: ${repair.oldCounter} -> ${repair.newCounter}`);
            }
            else {
                errors.push('Sequence auto-repair failed');
            }
        }
    }
    catch (err) {
        errors.push(`Sequence check failed: ${String(err)}`);
    }
    // 2. Verify task data can be queried
    try {
        const count = await accessor.countTasks();
        if (count < 0) {
            errors.push('Task count returned negative value');
        }
    }
    catch (err) {
        errors.push(`Task data query failed: ${String(err)}`);
    }
    try {
        const sessions = await accessor.loadSessions();
        if (!Array.isArray(sessions)) {
            errors.push('Sessions data is not an array');
        }
    }
    catch (err) {
        errors.push(`Sessions load failed: ${String(err)}`);
    }
    // 3. Check for checkpoint recency
    if (stats.lastCheckpoint) {
        const minutesSinceCheckpoint = (Date.now() - stats.lastCheckpoint.getTime()) / 60000;
        if (minutesSinceCheckpoint > 60) {
            warnings.push(`Last checkpoint was ${Math.round(minutesSinceCheckpoint)} minutes ago`);
        }
    }
    return {
        passed: errors.length === 0,
        errors,
        warnings,
        stats: getSafetyStats(),
    };
}
/**
 * Force immediate checkpoint.
 * Use before destructive operations.
 */
export async function forceSafetyCheckpoint(context, cwd) {
    log.info({ context }, 'Forcing checkpoint');
    await gitCheckpoint('manual', context, cwd);
    vacuumIntoBackup({ cwd, force: true }).catch(() => { }); // non-fatal SQLite snapshot
}
/**
 * Disable all safety for current process.
 * DANGEROUS - only use for recovery operations.
 */
export function disableSafety() {
    log.warn('All safety checks disabled - emergency recovery mode');
    // Set all safety options to false
    Object.assign(DEFAULT_SAFETY, {
        verify: false,
        checkpoint: false,
        validateSequence: false,
        strict: false,
    });
}
/**
 * Re-enable safety after being disabled.
 */
export function enableSafety() {
    log.info('Safety checks re-enabled');
    Object.assign(DEFAULT_SAFETY, {
        verify: true,
        checkpoint: true,
        validateSequence: true,
        strict: true,
    });
}
//# sourceMappingURL=data-safety-central.js.map