import type {
  Channel,
  RoutingConfig,
  ApplicationConfig,
  RetryPolicy,
} from '@zyphon/shared-types';
import type { CircuitBreakerManager } from './circuit-breaker.js';

export interface ResolvedRoute {
  channel: Channel;
  candidateProviders: string[];
  activeProviders: string[]; // Open circuits filtered out
  retryPolicy: RetryPolicy;
  timeoutMs: number;
}

export class RoutingEngine {
  private routingConfig: RoutingConfig;
  private circuitBreaker: CircuitBreakerManager;
  private roundRobinIndex: Partial<Record<Channel, number>> = {};

  constructor(routingConfig: RoutingConfig, circuitBreaker: CircuitBreakerManager) {
    this.routingConfig = routingConfig;
    this.circuitBreaker = circuitBreaker;
  }

  public updateConfig(newConfig: RoutingConfig): void {
    this.routingConfig = newConfig;
  }

  public getConfig(): RoutingConfig {
    return this.routingConfig;
  }

  /**
   * Resolve provider chain for a message dispatch.
   */
  public resolveRoute(app: ApplicationConfig, channel: Channel): ResolvedRoute {
    // 1. Determine candidate providers: App override bindings or Default channel chain
    let candidateProviders: string[] = [];

    if (app.bindings && app.bindings[channel] && app.bindings[channel]!.length > 0) {
      candidateProviders = [...app.bindings[channel]!];
    } else if (this.routingConfig.default && this.routingConfig.default[channel]) {
      candidateProviders = [...this.routingConfig.default[channel]];
    }

    // 2. Filter out providers whose circuit breaker is in OPEN state
    let activeProviders = candidateProviders.filter((providerId) => {
      const state = this.circuitBreaker.getState(providerId);
      return state !== 'OPEN';
    });

    const strategy = this.routingConfig.strategies?.[channel] || 'failover';
    if (strategy === 'round-robin' && activeProviders.length > 1) {
      const idx = this.roundRobinIndex[channel] ?? 0;
      const rotated = [...activeProviders.slice(idx), ...activeProviders.slice(0, idx)];
      activeProviders = rotated;
      this.roundRobinIndex[channel] = (idx + 1) % candidateProviders.length;
    }

    // 3. Resolve timeout
    const timeoutMs = this.routingConfig.timeout_ms?.[channel] ?? 5000;

    // 4. Resolve retry policy
    const retryPolicy: RetryPolicy = {
      max_attempts: this.routingConfig.retry?.max_attempts ?? 3,
      backoff_ms: this.routingConfig.retry?.backoff_ms ?? 100,
    };

    return {
      channel,
      candidateProviders,
      activeProviders,
      retryPolicy,
      timeoutMs,
    };
  }
}
