/**
 * HealthMetricsStore -- Storage abstraction for destination health metrics.
 *
 * v1 ships an in-memory implementation only. Future drop-ins:
 *   - RedisHealthMetricsStore   (key-prefix: governance:health:<adapterId>)
 *   - PrismaHealthMetricsStore  (table: destination_health_records)
 *
 * Limitation (v1): all data is lost on process restart. Use the Redis or
 * Prisma backing store in production deployments that require persistence.
 *
 * @connexum/ai-governance
 */
const RESERVOIR_SIZE = 1024;
/** Create a fresh LatencyStats accumulator. */
export function createLatencyStats() {
    return { count: 0, meanMs: 0, _m2: 0, _reservoir: [], _reservoirCursor: 0 };
}
/**
 * Record one latency sample using Welford's online algorithm.
 * Reservoir is updated via Algorithm R for order-independent sampling.
 */
export function recordLatencySample(stats, latencyMs) {
    stats.count += 1;
    const delta = latencyMs - stats.meanMs;
    stats.meanMs += delta / stats.count;
    const delta2 = latencyMs - stats.meanMs;
    stats._m2 += delta * delta2;
    // Reservoir sampling (Algorithm R)
    if (stats._reservoir.length < RESERVOIR_SIZE) {
        stats._reservoir.push(latencyMs);
    }
    else {
        // Replace a random existing sample with decreasing probability
        const j = Math.floor(Math.random() * stats.count);
        if (j < RESERVOIR_SIZE) {
            stats._reservoir[j] = latencyMs;
        }
    }
}
/**
 * Compute an approximate percentile from the reservoir sample.
 * Returns null when no samples have been recorded yet.
 */
export function estimatePercentile(stats, p) {
    if (stats._reservoir.length === 0)
        return null;
    const sorted = stats._reservoir.slice().sort((a, b) => a - b);
    const idx = Math.min(Math.floor((p / 100) * sorted.length), sorted.length - 1);
    return sorted[idx];
}
// ---------------------------------------------------------------------------
// InMemoryHealthMetricsStore -- default implementation
// ---------------------------------------------------------------------------
/** Build a DestinationHealthSummary snapshot from a mutable record. */
function toSummary(r) {
    return {
        adapterId: r.adapterId,
        registeredAt: new Date(r.registeredAt).toISOString(),
        totalAttempted: r.totalAttempted,
        totalSucceeded: r.totalSucceeded,
        totalFailed: r.totalFailed,
        totalRetried: r.totalRetried,
        dlqDepth: r.dlqDepth,
        latency: {
            count: r.latency.count,
            meanMs: r.latency.meanMs,
            p50Ms: estimatePercentile(r.latency, 50),
            p99Ms: estimatePercentile(r.latency, 99),
        },
        packAttempts: Object.fromEntries(r.packAttempts),
        lastErrorAt: r.lastErrorAt !== null ? new Date(r.lastErrorAt).toISOString() : null,
        lastErrorMessage: r.lastErrorMessage,
        lastSuccessAt: r.lastSuccessAt !== null ? new Date(r.lastSuccessAt).toISOString() : null,
        circuitBreakerState: r.circuitBreakerState,
    };
}
/** Create a fresh AdapterHealthRecord for the given adapterId. */
function createRecord(adapterId) {
    return {
        adapterId,
        registeredAt: Date.now(),
        totalAttempted: 0,
        totalSucceeded: 0,
        totalFailed: 0,
        totalRetried: 0,
        dlqDepth: 0,
        latency: createLatencyStats(),
        packAttempts: new Map(),
        lastErrorAt: null,
        lastErrorMessage: null,
        lastSuccessAt: null,
        circuitBreakerState: null,
    };
}
/**
 * Default in-memory implementation of HealthMetricsStore.
 *
 * All data is process-local and lost on restart.
 * Swap for RedisHealthMetricsStore or PrismaHealthMetricsStore in production.
 */
export class InMemoryHealthMetricsStore {
    records = new Map();
    register(adapterId) {
        if (!this.records.has(adapterId)) {
            this.records.set(adapterId, createRecord(adapterId));
        }
    }
    getMutable(adapterId) {
        return this.records.get(adapterId);
    }
    getSummary(adapterId) {
        const r = this.records.get(adapterId);
        return r ? toSummary(r) : null;
    }
    getAllSummaries() {
        return Array.from(this.records.values()).map(toSummary);
    }
    reset(adapterId) {
        if (this.records.has(adapterId)) {
            this.records.set(adapterId, createRecord(adapterId));
        }
    }
}
//# sourceMappingURL=health-metrics-store.js.map