/**
 * Circuit Breaker Utility
 * Robust circuit breaker implementation untuk database operations dan critical services
 * Menggunakan opossum library
 */

const CircuitBreaker = require('opossum');

/**
 * Circuit Breaker Manager
 * Manages multiple circuit breakers untuk different operations
 */
class CircuitBreakerManager {
	constructor() {
		this.breakers = new Map();
		this.stats = {
			totalBreakers: 0,
			openBreakers: 0,
			halfOpenBreakers: 0,
			closedBreakers: 0,
		};
	}

	/**
	 * Create or get circuit breaker
	 * @param {string} name - Circuit breaker name
	 * @param {Function} action - Function to wrap
	 * @param {Object} options - Circuit breaker options
	 * @returns {CircuitBreaker} Circuit breaker instance
	 */
	createBreaker(name, action, options = {}) {
		// If breaker already exists, return it
		if (this.breakers.has(name)) {
			return this.breakers.get(name);
		}

		// Default options
		const defaultOptions = {
			timeout: options.timeout || 5000, // 5 seconds timeout
			errorThresholdPercentage: options.errorThresholdPercentage || 50, // Open circuit if 50% errors
			resetTimeout: options.resetTimeout || 30000, // Try recovery after 30 seconds
			rollingCountTimeout: options.rollingCountTimeout || 60000, // 1 minute window
			rollingCountBuckets: options.rollingCountBuckets || 10, // 10 buckets
			name: name,
			enabled: options.enabled !== false, // Enabled by default
		};

		// Create circuit breaker
		const breaker = new CircuitBreaker(action, defaultOptions);

		// Setup event listeners
		this.setupBreakerEvents(breaker, name);

		// Store breaker
		this.breakers.set(name, breaker);
		this.stats.totalBreakers++;

		// Update stats
		this.updateStats();

		return breaker;
	}

	/**
	 * Setup event listeners for circuit breaker
	 * @param {CircuitBreaker} breaker - Circuit breaker instance
	 * @param {string} name - Circuit breaker name
	 */
	setupBreakerEvents(breaker, name) {
		breaker.on('open', () => {
			console.warn(`[CircuitBreaker] ${name} - Circuit OPEN (fast failing)`);
			this.updateStats();
		});

		breaker.on('halfOpen', () => {
			console.log(`[CircuitBreaker] ${name} - Circuit HALF-OPEN (testing recovery)`);
			this.updateStats();
		});

		breaker.on('close', () => {
			console.log(`[CircuitBreaker] ${name} - Circuit CLOSED (normal operation)`);
			this.updateStats();
		});

		breaker.on('failure', (error) => {
			console.error(`[CircuitBreaker] ${name} - Failure:`, error.message);
		});

		breaker.on('success', () => {
			// Silent success (only log if needed)
		});

		breaker.on('reject', () => {
			console.warn(`[CircuitBreaker] ${name} - Request REJECTED (circuit open)`);
		});

		breaker.on('timeout', () => {
			console.warn(`[CircuitBreaker] ${name} - Request TIMEOUT`);
		});
	}

	/**
	 * Get circuit breaker by name
	 * @param {string} name - Circuit breaker name
	 * @returns {CircuitBreaker|null} Circuit breaker instance or null
	 */
	getBreaker(name) {
		return this.breakers.get(name) || null;
	}

	/**
	 * Update statistics
	 */
	updateStats() {
		let open = 0;
		let halfOpen = 0;
		let closed = 0;

		this.breakers.forEach((breaker) => {
			const state = breaker.status?.state || 'closed';
			if (state === 'open') open++;
			else if (state === 'halfOpen') halfOpen++;
			else closed++;
		});

		this.stats.openBreakers = open;
		this.stats.halfOpenBreakers = halfOpen;
		this.stats.closedBreakers = closed;
	}

	/**
	 * Get all circuit breaker statistics
	 * @returns {Object} Statistics object
	 */
	getAllStats() {
		this.updateStats();

		const breakerStats = {};
		this.breakers.forEach((breaker, name) => {
			const status = breaker.status || {};
			breakerStats[name] = {
				state: status.state || 'closed',
				enabled: breaker.enabled !== false,
				stats: {
					fires: status.stats?.fires || 0,
					cacheHits: status.stats?.cacheHits || 0,
					cacheMisses: status.stats?.cacheMisses || 0,
					rejects: status.stats?.rejects || 0,
					failures: status.stats?.failures || 0,
					timeouts: status.stats?.timeouts || 0,
					successes: status.stats?.successes || 0,
					fallbacks: status.stats?.fallbacks || 0,
					semaphoreRejections: status.stats?.semaphoreRejections || 0,
				},
				options: {
					timeout: breaker.options?.timeout,
					errorThresholdPercentage: breaker.options?.errorThresholdPercentage,
					resetTimeout: breaker.options?.resetTimeout,
				},
				window: {
					rollingCountTimeout: breaker.options?.rollingCountTimeout,
					rollingCountBuckets: breaker.options?.rollingCountBuckets,
				},
			};
		});

		return {
			summary: this.stats,
			breakers: breakerStats,
		};
	}

	/**
	 * Reset circuit breaker
	 * @param {string} name - Circuit breaker name
	 */
	resetBreaker(name) {
		const breaker = this.breakers.get(name);
		if (breaker) {
			breaker.close();
			console.log(`[CircuitBreaker] ${name} - Manually reset`);
		}
	}

	/**
	 * Reset all circuit breakers
	 */
	resetAll() {
		this.breakers.forEach((breaker, name) => {
			breaker.close();
		});
		console.log('[CircuitBreaker] All circuit breakers reset');
	}
}

// Singleton instance
let circuitBreakerManager = null;

/**
 * Get circuit breaker manager instance
 * @returns {CircuitBreakerManager} Circuit breaker manager
 */
function getCircuitBreakerManager() {
	if (!circuitBreakerManager) {
		circuitBreakerManager = new CircuitBreakerManager();
	}
	return circuitBreakerManager;
}

module.exports = {
	CircuitBreakerManager,
	getCircuitBreakerManager,
};
