const { logger } = require('./logger');

class CircuitBreaker {
  constructor(options = {}) {
    this.name = options.name || 'default';
    this.failureThreshold = options.failureThreshold || 5;
    this.resetTimeout = options.resetTimeout || 30000;
    this.failures = 0;
    this.state = 'CLOSED';
    this.nextAttempt = Date.now();
  }

  async execute(fn) {
    if (this.state === 'OPEN') {
      if (Date.now() < this.nextAttempt) {
        logger.warn({ circuitBreaker: this.name, state: this.state }, 'Circuit breaker is OPEN, rejecting request');
        throw new Error(`Circuit breaker ${this.name} is OPEN`);
      }
      this.state = 'HALF-OPEN';
      logger.info({ circuitBreaker: this.name }, 'Circuit breaker moved to HALF-OPEN');
    }

    try {
      const result = await fn();
      this.onSuccess();
      return result;
    } catch (error) {
      this.onFailure(error);
      throw error;
    }
  }

  onSuccess() {
    if (this.state === 'HALF-OPEN') {
      logger.info({ circuitBreaker: this.name }, 'Circuit breaker recovered, moving to CLOSED');
    }
    this.failures = 0;
    this.state = 'CLOSED';
  }

  onFailure(error) {
    this.failures++;
    logger.warn({
      circuitBreaker: this.name,
      failures: this.failures,
      threshold: this.failureThreshold,
      error: error.message,
    }, 'Circuit breaker recorded failure');

    if (this.failures >= this.failureThreshold) {
      this.state = 'OPEN';
      this.nextAttempt = Date.now() + this.resetTimeout;
      logger.error({
        circuitBreaker: this.name,
        resetAt: new Date(this.nextAttempt).toISOString(),
      }, 'Circuit breaker tripped to OPEN');
    }
  }

  getState() {
    return {
      name: this.name,
      state: this.state,
      failures: this.failures,
      nextAttempt: this.state === 'OPEN' ? new Date(this.nextAttempt).toISOString() : null,
    };
  }
}

const circuitBreakers = new Map();

function getCircuitBreaker(name, options = {}) {
  if (!circuitBreakers.has(name)) {
    circuitBreakers.set(name, new CircuitBreaker({ name, ...options }));
  }
  return circuitBreakers.get(name);
}

module.exports = { CircuitBreaker, getCircuitBreaker };
