/**
 * Circuit Breaker for External API Calls
 * Prevents cascading failures and provides resilience
 */

import CircuitBreaker from 'opossum';
import { createLogger } from '@4runr/shared';

const logger = createLogger('Gateway:CircuitBreaker');

export interface CircuitBreakerOptions {
  timeout?: number; // Timeout in ms
  errorThresholdPercentage?: number; // % of failures before opening
  resetTimeout?: number; // Time before attempting to close circuit
  enabled?: boolean; // Enable/disable circuit breaker
}

const DEFAULT_OPTIONS: CircuitBreakerOptions = {
  timeout: 30000, // 30 seconds
  errorThresholdPercentage: 50, // Open after 50% failures
  resetTimeout: 60000, // 1 minute before retry
  enabled: true
};

/**
 * Create a circuit breaker for an async function
 */
export function createCircuitBreaker<T extends any[], R>(
  fn: (...args: T) => Promise<R>,
  serviceName: string,
  options: CircuitBreakerOptions = {}
): CircuitBreaker<T, R> {
  const config = { ...DEFAULT_OPTIONS, ...options };

  if (!config.enabled) {
    // Return a passthrough function if disabled
    return {
      fire: async (...args: T) => fn(...args),
      open: false,
      halfOpen: false,
      closed: true
    } as any;
  }

  const breaker = new CircuitBreaker(fn, {
    timeout: config.timeout,
    errorThresholdPercentage: config.errorThresholdPercentage,
    resetTimeout: config.resetTimeout,
    name: serviceName
  });

  // Log circuit breaker events
  breaker.on('open', () => {
    logger.warn('Circuit breaker opened', { service: serviceName });
  });

  breaker.on('halfOpen', () => {
    logger.info('Circuit breaker half-open', { service: serviceName });
  });

  breaker.on('close', () => {
    logger.info('Circuit breaker closed', { service: serviceName });
  });

  breaker.on('failure', (error: Error) => {
    logger.error('Circuit breaker failure', { 
      service: serviceName, 
      error: error.message 
    });
  });

  breaker.on('success', () => {
    logger.debug('Circuit breaker success', { service: serviceName });
  });

  return breaker;
}

/**
 * Circuit breaker for OpenAI API calls
 */
export const openAICircuitBreaker = createCircuitBreaker(
  async (fn: () => Promise<any>) => fn(),
  'OpenAI',
  {
    timeout: 60000, // 60 seconds for OpenAI
    errorThresholdPercentage: 50,
    resetTimeout: 120000 // 2 minutes
  }
);

/**
 * Circuit breaker for Anthropic API calls
 */
export const anthropicCircuitBreaker = createCircuitBreaker(
  async (fn: () => Promise<any>) => fn(),
  'Anthropic',
  {
    timeout: 60000,
    errorThresholdPercentage: 50,
    resetTimeout: 120000
  }
);

/**
 * Circuit breaker for HTTP requests
 */
export const httpCircuitBreaker = createCircuitBreaker(
  async (fn: () => Promise<any>) => fn(),
  'HTTP',
  {
    timeout: 10000, // 10 seconds for HTTP
    errorThresholdPercentage: 50,
    resetTimeout: 60000
  }
);

/**
 * Execute with circuit breaker
 */
export async function executeWithCircuitBreaker<T>(
  breaker: CircuitBreaker<any[], T>,
  fn: () => Promise<T>
): Promise<T> {
  try {
    return await breaker.fire(fn);
  } catch (error: any) {
    if (error.code === 'ECIRCUITOPEN') {
      throw new Error(`Service temporarily unavailable (circuit open): ${error.message}`);
    }
    throw error;
  }
}

