// SPDX-License-Identifier: Apache-2.0
// Copyright 2024-2026 Vorion LLC
import { getRedis } from './redis.js';
import { createLogger } from './logger.js';
const logger = createLogger({ component: 'circuit-breaker' });
/**
 * Default configurations per service type.
 * These can be overridden via environment variables in config.ts.
 */
export const CIRCUIT_BREAKER_CONFIGS = {
    database: {
        name: 'database',
        failureThreshold: 5,
        resetTimeoutMs: 30000,
        halfOpenMaxAttempts: 3,
        monitorWindowMs: 60000,
    },
    redis: {
        name: 'redis',
        failureThreshold: 10,
        resetTimeoutMs: 10000,
        halfOpenMaxAttempts: 5,
        monitorWindowMs: 30000,
    },
    webhook: {
        name: 'webhook',
        failureThreshold: 3,
        resetTimeoutMs: 60000,
        halfOpenMaxAttempts: 2,
        monitorWindowMs: 120000,
    },
    policyEngine: {
        name: 'policy-engine',
        failureThreshold: 5,
        resetTimeoutMs: 15000,
        halfOpenMaxAttempts: 3,
        monitorWindowMs: 60000,
    },
    trustEngine: {
        name: 'trust-engine',
        failureThreshold: 5,
        resetTimeoutMs: 30000,
        halfOpenMaxAttempts: 2,
        monitorWindowMs: 60000,
    },
    auditService: {
        name: 'audit-service',
        failureThreshold: 10,
        resetTimeoutMs: 15000,
        halfOpenMaxAttempts: 3,
        monitorWindowMs: 60000,
    },
    gdprService: {
        name: 'gdpr-service',
        failureThreshold: 5,
        resetTimeoutMs: 30000,
        halfOpenMaxAttempts: 2,
        monitorWindowMs: 60000,
    },
    consentService: {
        name: 'consent-service',
        failureThreshold: 5,
        resetTimeoutMs: 20000,
        halfOpenMaxAttempts: 2,
        monitorWindowMs: 60000,
    },
    // HSM providers - critical security infrastructure
    hsm: {
        name: 'hsm',
        failureThreshold: 3, // Lower threshold - HSM failures are critical
        resetTimeoutMs: 60000, // 1 minute - HSM recovery can take time
        halfOpenMaxAttempts: 1, // Single test per attempt
        monitorWindowMs: 120000, // 2 minute window
    },
    hsmAws: {
        name: 'hsm-aws',
        failureThreshold: 3,
        resetTimeoutMs: 45000,
        halfOpenMaxAttempts: 2,
        monitorWindowMs: 90000,
    },
    hsmAzure: {
        name: 'hsm-azure',
        failureThreshold: 3,
        resetTimeoutMs: 45000,
        halfOpenMaxAttempts: 2,
        monitorWindowMs: 90000,
    },
    hsmGcp: {
        name: 'hsm-gcp',
        failureThreshold: 3,
        resetTimeoutMs: 45000,
        halfOpenMaxAttempts: 2,
        monitorWindowMs: 90000,
    },
    hsmThales: {
        name: 'hsm-thales',
        failureThreshold: 3,
        resetTimeoutMs: 60000,
        halfOpenMaxAttempts: 1,
        monitorWindowMs: 120000,
    },
    // SIEM connectors - monitoring infrastructure
    siemSplunk: {
        name: 'siem-splunk',
        failureThreshold: 5,
        resetTimeoutMs: 30000,
        halfOpenMaxAttempts: 3,
        monitorWindowMs: 60000,
    },
    siemElastic: {
        name: 'siem-elastic',
        failureThreshold: 5,
        resetTimeoutMs: 30000,
        halfOpenMaxAttempts: 3,
        monitorWindowMs: 60000,
    },
    siemDatadog: {
        name: 'siem-datadog',
        failureThreshold: 5,
        resetTimeoutMs: 20000,
        halfOpenMaxAttempts: 3,
        monitorWindowMs: 60000,
    },
    // Email and notification services
    emailSes: {
        name: 'email-ses',
        failureThreshold: 5,
        resetTimeoutMs: 30000,
        halfOpenMaxAttempts: 2,
        monitorWindowMs: 60000,
    },
    emailSmtp: {
        name: 'email-smtp',
        failureThreshold: 5,
        resetTimeoutMs: 45000,
        halfOpenMaxAttempts: 2,
        monitorWindowMs: 60000,
    },
    snsNotification: {
        name: 'sns-notification',
        failureThreshold: 5,
        resetTimeoutMs: 20000,
        halfOpenMaxAttempts: 3,
        monitorWindowMs: 60000,
    },
    // IP reputation and threat intelligence
    ipReputation: {
        name: 'ip-reputation',
        failureThreshold: 10, // Higher tolerance - non-critical
        resetTimeoutMs: 15000,
        halfOpenMaxAttempts: 3,
        monitorWindowMs: 30000,
    },
    threatIntel: {
        name: 'threat-intel',
        failureThreshold: 10,
        resetTimeoutMs: 15000,
        halfOpenMaxAttempts: 3,
        monitorWindowMs: 30000,
    },
    // DLP and data protection
    dlpScanner: {
        name: 'dlp-scanner',
        failureThreshold: 5,
        resetTimeoutMs: 30000,
        halfOpenMaxAttempts: 2,
        monitorWindowMs: 60000,
    },
    // Bot detection
    botDetection: {
        name: 'bot-detection',
        failureThreshold: 10,
        resetTimeoutMs: 15000,
        halfOpenMaxAttempts: 3,
        monitorWindowMs: 30000,
    },
};
/**
 * Circuit Breaker implementation with Redis-backed state for distributed systems
 *
 * @example
 * ```typescript
 * const breaker = new CircuitBreaker({
 *   name: 'policy-evaluator',
 *   failureThreshold: 5,
 *   resetTimeoutMs: 30000,
 * });
 *
 * const result = await breaker.execute(async () => {
 *   return await policyEvaluator.evaluateMultiple(policies, context);
 * });
 *
 * if (result.circuitOpen) {
 *   // Use fallback logic
 * }
 * ```
 */
export class CircuitBreaker {
    name;
    failureThreshold;
    resetTimeoutMs;
    halfOpenMaxAttempts;
    monitorWindowMs;
    redis;
    onStateChange;
    // Local cache to reduce Redis calls for frequently checked state
    localStateCache = null;
    localStateCacheTime = 0;
    localStateCacheTtlMs = 1000; // 1 second local cache
    constructor(options) {
        this.name = options.name;
        this.failureThreshold = options.failureThreshold ?? 5;
        this.resetTimeoutMs = options.resetTimeoutMs ?? 30000;
        this.halfOpenMaxAttempts = options.halfOpenMaxAttempts ?? 3;
        this.monitorWindowMs = options.monitorWindowMs ?? 60000;
        this.redis = options.redis ?? getRedis();
        this.onStateChange = options.onStateChange;
    }
    /**
     * Get Redis key for this circuit breaker
     */
    getRedisKey() {
        return `vorion:circuit-breaker:${this.name}`;
    }
    /**
     * Get the current state from Redis
     */
    async getState() {
        // Check local cache first
        const now = Date.now();
        if (this.localStateCache && (now - this.localStateCacheTime) < this.localStateCacheTtlMs) {
            return this.localStateCache;
        }
        try {
            const data = await this.redis.get(this.getRedisKey());
            if (!data) {
                const initialState = {
                    state: 'CLOSED',
                    failureCount: 0,
                    lastFailureTime: null,
                    openedAt: null,
                    halfOpenAttempts: 0,
                    windowStartTime: null,
                };
                // Update local cache
                this.localStateCache = initialState;
                this.localStateCacheTime = now;
                return initialState;
            }
            const state = JSON.parse(data);
            // Update local cache
            this.localStateCache = state;
            this.localStateCacheTime = now;
            return state;
        }
        catch (error) {
            logger.error({ error, name: this.name }, 'Failed to get circuit breaker state from Redis');
            // Return default closed state on error - fail open
            return {
                state: 'CLOSED',
                failureCount: 0,
                lastFailureTime: null,
                openedAt: null,
                halfOpenAttempts: 0,
                windowStartTime: null,
            };
        }
    }
    /**
     * Save state to Redis
     */
    async setState(state) {
        try {
            // Set with expiry of 24 hours to prevent stale data accumulation
            await this.redis.setex(this.getRedisKey(), 86400, // 24 hours
            JSON.stringify(state));
            // Update local cache
            this.localStateCache = state;
            this.localStateCacheTime = Date.now();
        }
        catch (error) {
            logger.error({ error, name: this.name }, 'Failed to save circuit breaker state to Redis');
        }
    }
    /**
     * Get the current circuit state
     */
    async getCircuitState() {
        const state = await this.getState();
        const now = Date.now();
        // Check if circuit should transition from OPEN to HALF_OPEN
        if (state.state === 'OPEN' && state.openedAt) {
            const timeSinceOpen = now - state.openedAt;
            if (timeSinceOpen >= this.resetTimeoutMs) {
                // Transition to HALF_OPEN, reset half-open attempts counter
                const newState = {
                    ...state,
                    state: 'HALF_OPEN',
                    halfOpenAttempts: 0,
                };
                await this.setState(newState);
                this.notifyStateChange(state.state, 'HALF_OPEN');
                logger.info({ name: this.name, timeSinceOpenMs: timeSinceOpen }, 'Circuit breaker transitioned to HALF_OPEN');
                return 'HALF_OPEN';
            }
        }
        return state.state;
    }
    /**
     * Check if the circuit is currently open (failing fast)
     */
    async isOpen() {
        const currentState = await this.getCircuitState();
        return currentState === 'OPEN';
    }
    /**
     * Record a successful call
     */
    async recordSuccess() {
        const state = await this.getState();
        if (state.state === 'HALF_OPEN') {
            // Successful call in HALF_OPEN state - close the circuit
            const newState = {
                state: 'CLOSED',
                failureCount: 0,
                lastFailureTime: null,
                openedAt: null,
                halfOpenAttempts: 0,
                windowStartTime: null,
            };
            await this.setState(newState);
            this.notifyStateChange('HALF_OPEN', 'CLOSED');
            logger.info({ name: this.name }, 'Circuit breaker closed after successful recovery');
        }
        else if (state.state === 'CLOSED' && state.failureCount > 0) {
            // Reset failure count on success in CLOSED state
            const newState = {
                ...state,
                failureCount: 0,
                lastFailureTime: null,
                windowStartTime: null,
            };
            await this.setState(newState);
        }
    }
    /**
     * Record a failed call
     */
    async recordFailure() {
        const state = await this.getState();
        const now = Date.now();
        if (state.state === 'HALF_OPEN') {
            // Increment half-open attempts
            const newHalfOpenAttempts = (state.halfOpenAttempts || 0) + 1;
            if (newHalfOpenAttempts >= this.halfOpenMaxAttempts) {
                // Max half-open attempts reached - reopen the circuit
                const newState = {
                    state: 'OPEN',
                    failureCount: state.failureCount + 1,
                    lastFailureTime: now,
                    openedAt: now,
                    halfOpenAttempts: 0,
                    windowStartTime: null,
                };
                await this.setState(newState);
                this.notifyStateChange('HALF_OPEN', 'OPEN');
                logger.warn({ name: this.name, halfOpenAttempts: newHalfOpenAttempts, maxAttempts: this.halfOpenMaxAttempts }, 'Circuit breaker reopened after max half-open attempts exceeded');
            }
            else {
                // Still in HALF_OPEN, increment attempt counter
                const newState = {
                    ...state,
                    failureCount: state.failureCount + 1,
                    lastFailureTime: now,
                    halfOpenAttempts: newHalfOpenAttempts,
                };
                await this.setState(newState);
                logger.debug({ name: this.name, halfOpenAttempts: newHalfOpenAttempts, maxAttempts: this.halfOpenMaxAttempts }, 'Circuit breaker recorded failure in HALF_OPEN state');
            }
        }
        else if (state.state === 'CLOSED') {
            // Check if we need to start a new monitoring window
            let windowStartTime = state.windowStartTime;
            let failureCount = state.failureCount;
            if (windowStartTime && now - windowStartTime > this.monitorWindowMs) {
                // Window has expired, reset failure count and start new window
                windowStartTime = now;
                failureCount = 0;
                logger.debug({ name: this.name, windowMs: this.monitorWindowMs }, 'Circuit breaker monitoring window expired, resetting failure count');
            }
            else if (!windowStartTime) {
                // Start new window
                windowStartTime = now;
            }
            const newFailureCount = failureCount + 1;
            if (newFailureCount >= this.failureThreshold) {
                // Threshold exceeded - open the circuit
                const newState = {
                    state: 'OPEN',
                    failureCount: newFailureCount,
                    lastFailureTime: now,
                    openedAt: now,
                    halfOpenAttempts: 0,
                    windowStartTime: null,
                };
                await this.setState(newState);
                this.notifyStateChange('CLOSED', 'OPEN');
                logger.warn({ name: this.name, failureCount: newFailureCount, threshold: this.failureThreshold }, 'Circuit breaker opened due to failure threshold exceeded');
            }
            else {
                // Increment failure count
                const newState = {
                    ...state,
                    failureCount: newFailureCount,
                    lastFailureTime: now,
                    windowStartTime,
                };
                await this.setState(newState);
                logger.debug({ name: this.name, failureCount: newFailureCount, threshold: this.failureThreshold }, 'Circuit breaker recorded failure');
            }
        }
        // If circuit is already OPEN, no action needed
    }
    /**
     * Execute a function with circuit breaker protection
     *
     * @param fn - The function to execute
     * @returns Result including whether circuit was open
     */
    async execute(fn) {
        const currentState = await this.getCircuitState();
        if (currentState === 'OPEN') {
            logger.debug({ name: this.name }, 'Circuit breaker is OPEN, failing fast');
            return {
                success: false,
                circuitOpen: true,
                error: new Error(`Circuit breaker '${this.name}' is OPEN`),
            };
        }
        try {
            const result = await fn();
            await this.recordSuccess();
            return {
                success: true,
                result,
                circuitOpen: false,
            };
        }
        catch (error) {
            await this.recordFailure();
            return {
                success: false,
                error: error instanceof Error ? error : new Error(String(error)),
                circuitOpen: false,
            };
        }
    }
    /**
     * Force the circuit to open state
     * Useful for manual intervention or testing
     */
    async forceOpen() {
        const state = await this.getState();
        const previousState = state.state;
        const newState = {
            state: 'OPEN',
            failureCount: this.failureThreshold,
            lastFailureTime: Date.now(),
            openedAt: Date.now(),
            halfOpenAttempts: 0,
            windowStartTime: null,
        };
        await this.setState(newState);
        if (previousState !== 'OPEN') {
            this.notifyStateChange(previousState, 'OPEN');
        }
        logger.info({ name: this.name, previousState }, 'Circuit breaker forcibly opened');
    }
    /**
     * Force the circuit to closed state
     * Useful for manual intervention or testing
     */
    async forceClose() {
        const state = await this.getState();
        const previousState = state.state;
        const newState = {
            state: 'CLOSED',
            failureCount: 0,
            lastFailureTime: null,
            openedAt: null,
            halfOpenAttempts: 0,
            windowStartTime: null,
        };
        await this.setState(newState);
        if (previousState !== 'CLOSED') {
            this.notifyStateChange(previousState, 'CLOSED');
        }
        logger.info({ name: this.name, previousState }, 'Circuit breaker forcibly closed');
    }
    /**
     * Reset the circuit breaker state completely
     */
    async reset() {
        try {
            await this.redis.del(this.getRedisKey());
            this.localStateCache = null;
            this.localStateCacheTime = 0;
            logger.info({ name: this.name }, 'Circuit breaker reset');
        }
        catch (error) {
            logger.error({ error, name: this.name }, 'Failed to reset circuit breaker');
        }
    }
    /**
     * Get detailed status information for monitoring
     */
    async getStatus() {
        const state = await this.getState();
        const now = Date.now();
        let timeUntilReset = null;
        if (state.state === 'OPEN' && state.openedAt) {
            const elapsed = now - state.openedAt;
            timeUntilReset = Math.max(0, this.resetTimeoutMs - elapsed);
        }
        return {
            name: this.name,
            state: state.state,
            failureCount: state.failureCount,
            failureThreshold: this.failureThreshold,
            resetTimeoutMs: this.resetTimeoutMs,
            halfOpenMaxAttempts: this.halfOpenMaxAttempts,
            halfOpenAttempts: state.halfOpenAttempts || 0,
            monitorWindowMs: this.monitorWindowMs,
            lastFailureTime: state.lastFailureTime ? new Date(state.lastFailureTime) : null,
            openedAt: state.openedAt ? new Date(state.openedAt) : null,
            timeUntilReset,
            windowStartTime: state.windowStartTime ? new Date(state.windowStartTime) : null,
        };
    }
    /**
     * Notify state change callback
     */
    notifyStateChange(from, to) {
        if (this.onStateChange) {
            try {
                this.onStateChange(from, to, this);
            }
            catch (error) {
                logger.error({ error, name: this.name, from, to }, 'Error in circuit breaker state change callback');
            }
        }
    }
}
/**
 * Factory function to create a circuit breaker with common defaults
 */
export function createCircuitBreaker(options) {
    return new CircuitBreaker(options);
}
// =============================================================================
// Circuit Breaker Registry
// =============================================================================
/**
 * Global circuit breaker registry for managing per-service circuit breakers.
 * Uses lazy initialization to create circuit breakers on first access.
 */
const circuitBreakers = new Map();
/**
 * Configuration overrides that can be set at startup
 */
let configOverrides = {};
/**
 * Set configuration overrides for circuit breakers.
 * This should be called during application initialization before any circuit breakers are used.
 *
 * @example
 * ```typescript
 * setCircuitBreakerConfigOverrides({
 *   database: { failureThreshold: 10, resetTimeoutMs: 60000 },
 *   redis: { failureThreshold: 20 },
 * });
 * ```
 */
export function setCircuitBreakerConfigOverrides(overrides) {
    configOverrides = overrides;
    logger.info({ services: Object.keys(overrides) }, 'Circuit breaker config overrides set');
}
/**
 * Get or create a circuit breaker for a specific service.
 * Uses the default configuration for the service type, with optional overrides.
 *
 * @param serviceName - The name of the service (e.g., 'database', 'redis', 'webhook')
 * @param onStateChange - Optional callback for state changes
 * @returns The circuit breaker instance for the service
 *
 * @example
 * ```typescript
 * const dbBreaker = getCircuitBreaker('database');
 * const result = await dbBreaker.execute(async () => {
 *   return await db.query('SELECT * FROM users');
 * });
 * ```
 */
export function getCircuitBreaker(serviceName, onStateChange) {
    if (!circuitBreakers.has(serviceName)) {
        // Get base config, fall back to database config for unknown services
        const baseConfig = CIRCUIT_BREAKER_CONFIGS[serviceName] || {
            ...CIRCUIT_BREAKER_CONFIGS.database,
            name: serviceName,
        };
        // Apply any config overrides
        const overrides = configOverrides[serviceName] || {};
        const finalConfig = {
            ...baseConfig,
            ...overrides,
            name: serviceName, // Always use the requested service name
        };
        const breaker = new CircuitBreaker({
            name: finalConfig.name,
            failureThreshold: finalConfig.failureThreshold,
            resetTimeoutMs: finalConfig.resetTimeoutMs,
            halfOpenMaxAttempts: finalConfig.halfOpenMaxAttempts,
            monitorWindowMs: finalConfig.monitorWindowMs,
            onStateChange,
        });
        circuitBreakers.set(serviceName, breaker);
        logger.debug({
            serviceName,
            config: {
                failureThreshold: finalConfig.failureThreshold,
                resetTimeoutMs: finalConfig.resetTimeoutMs,
                halfOpenMaxAttempts: finalConfig.halfOpenMaxAttempts,
                monitorWindowMs: finalConfig.monitorWindowMs,
            },
        }, 'Created circuit breaker for service');
    }
    return circuitBreakers.get(serviceName);
}
/**
 * Get all registered circuit breakers with their current status.
 * Useful for health checks and monitoring dashboards.
 *
 * @returns Map of service names to their circuit breaker status
 */
export async function getAllCircuitBreakerStatuses() {
    const statuses = new Map();
    const entries = Array.from(circuitBreakers);
    for (const [serviceName, breaker] of entries) {
        statuses.set(serviceName, await breaker.getStatus());
    }
    return statuses;
}
/**
 * Reset a specific circuit breaker by service name.
 * Useful for manual recovery during incidents.
 *
 * @param serviceName - The service name to reset
 * @returns True if the circuit breaker was found and reset
 */
export async function resetCircuitBreaker(serviceName) {
    const breaker = circuitBreakers.get(serviceName);
    if (!breaker) {
        return false;
    }
    await breaker.reset();
    logger.info({ serviceName }, 'Circuit breaker reset via registry');
    return true;
}
/**
 * Clear all circuit breakers from the registry.
 * Primarily for testing purposes.
 */
export function clearCircuitBreakerRegistry() {
    circuitBreakers.clear();
    configOverrides = {};
    logger.debug({}, 'Circuit breaker registry cleared');
}
// =============================================================================
// Circuit Breaker Open Error
// =============================================================================
/**
 * Error thrown when a circuit breaker is open and blocking execution.
 * This error allows callers to distinguish circuit breaker rejections
 * from actual execution failures.
 */
export class CircuitBreakerOpenError extends Error {
    serviceName;
    circuitState;
    constructor(serviceName, circuitState = 'OPEN') {
        super(`Circuit breaker '${serviceName}' is ${circuitState} - request rejected`);
        this.name = 'CircuitBreakerOpenError';
        this.serviceName = serviceName;
        this.circuitState = circuitState;
    }
}
/**
 * Default metrics callback (no-op)
 * Can be overridden via setCircuitBreakerMetricsCallback
 */
let metricsCallback = {
    recordStateChange: () => { },
    recordFailure: () => { },
    recordSuccess: () => { },
    updateState: () => { },
};
/**
 * Set the metrics callback for circuit breaker operations.
 * This should be called during application initialization to wire up
 * Prometheus metrics without creating circular dependencies.
 *
 * @param callback - The metrics callback functions
 */
export function setCircuitBreakerMetricsCallback(callback) {
    metricsCallback = callback;
    logger.debug({}, 'Circuit breaker metrics callback configured');
}
// =============================================================================
// withCircuitBreaker Utility Function
// =============================================================================
/**
 * Execute a function with circuit breaker protection.
 *
 * This is a convenience wrapper that:
 * 1. Gets or creates the appropriate circuit breaker for the service
 * 2. Executes the function through the circuit breaker
 * 3. Records metrics for state changes, successes, and failures
 * 4. Throws CircuitBreakerOpenError when the circuit is open
 *
 * @param serviceName - The service name identifier (must match CIRCUIT_BREAKER_CONFIGS keys)
 * @param fn - The async function to execute
 * @returns The result of the function
 * @throws CircuitBreakerOpenError when the circuit is open
 * @throws The original error if the function fails
 *
 * @example
 * ```typescript
 * // Protect a database call
 * const result = await withCircuitBreaker('database', async () => {
 *   return await db.query('SELECT * FROM users WHERE id = $1', [userId]);
 * });
 *
 * // Protect an external API call
 * try {
 *   const data = await withCircuitBreaker('gdprService', async () => {
 *     return await gdprApi.exportUserData(userId);
 *   });
 * } catch (error) {
 *   if (error instanceof CircuitBreakerOpenError) {
 *     // Handle circuit open - use fallback or return cached data
 *     return cachedData;
 *   }
 *   throw error;
 * }
 * ```
 */
export async function withCircuitBreaker(serviceName, fn) {
    // Get or create the circuit breaker for this service
    const breaker = getCircuitBreaker(serviceName, (from, to) => {
        // Record state change metric
        metricsCallback.recordStateChange(serviceName, from, to);
        logger.info({ service: serviceName, fromState: from, toState: to }, 'Circuit breaker state changed');
    });
    // Update the current state metric
    const currentState = await breaker.getCircuitState();
    metricsCallback.updateState(serviceName, currentState);
    // Execute through the circuit breaker
    const result = await breaker.execute(fn);
    // Handle the result
    if (result.circuitOpen) {
        // Circuit is open - throw CircuitBreakerOpenError
        logger.debug({ service: serviceName, state: 'OPEN' }, 'Circuit breaker rejected request');
        throw new CircuitBreakerOpenError(serviceName, 'OPEN');
    }
    if (result.success) {
        // Success - record metric
        metricsCallback.recordSuccess(serviceName);
        return result.result;
    }
    // Failure - record metric and rethrow
    metricsCallback.recordFailure(serviceName);
    if (result.error) {
        throw result.error;
    }
    // Should not reach here, but throw generic error just in case
    throw new Error(`Circuit breaker '${serviceName}' execution failed`);
}
/**
 * Execute a function with circuit breaker protection, returning a result object
 * instead of throwing on circuit open.
 *
 * This variant is useful when you want to handle circuit open gracefully
 * without try/catch.
 *
 * @param serviceName - The service name identifier
 * @param fn - The async function to execute
 * @returns Result object with success flag, result/error, and circuitOpen indicator
 *
 * @example
 * ```typescript
 * const result = await withCircuitBreakerResult('auditService', async () => {
 *   return await auditService.record(event);
 * });
 *
 * if (result.circuitOpen) {
 *   // Queue for retry later
 *   await queueForRetry(event);
 * } else if (!result.success) {
 *   logger.error({ error: result.error }, 'Audit failed');
 * }
 * ```
 */
export async function withCircuitBreakerResult(serviceName, fn) {
    // Get or create the circuit breaker for this service
    const breaker = getCircuitBreaker(serviceName, (from, to) => {
        metricsCallback.recordStateChange(serviceName, from, to);
        logger.info({ service: serviceName, fromState: from, toState: to }, 'Circuit breaker state changed');
    });
    // Update the current state metric
    const currentState = await breaker.getCircuitState();
    metricsCallback.updateState(serviceName, currentState);
    // Execute through the circuit breaker
    const result = await breaker.execute(fn);
    // Record metrics based on result
    if (!result.circuitOpen) {
        if (result.success) {
            metricsCallback.recordSuccess(serviceName);
        }
        else {
            metricsCallback.recordFailure(serviceName);
        }
    }
    return result;
}
//# sourceMappingURL=circuit-breaker.js.map