import type { ProviderRegistry } from '../core/provider-registry.js';
import type { EventBus } from '../realtime/event-bus.js';
import type { CircuitBreakerManager } from '../core/circuit-breaker.js';
import type { ProviderHealth } from '@zyphon/provider-contracts';

export class HealthMonitor {
  private providerRegistry: ProviderRegistry;
  private eventBus: EventBus;
  private circuitBreaker?: CircuitBreakerManager;
  private intervalMs: number;
  private timer: NodeJS.Timeout | null = null;
  private lastHealth = new Map<string, ProviderHealth['status']>();

  constructor(deps: {
    providerRegistry: ProviderRegistry;
    eventBus: EventBus;
    circuitBreaker?: CircuitBreakerManager;
    intervalMs?: number;
  }) {
    this.providerRegistry = deps.providerRegistry;
    this.eventBus = deps.eventBus;
    this.circuitBreaker = deps.circuitBreaker;
    this.intervalMs = deps.intervalMs || 15000;
  }

  public start(): void {
    if (this.timer) return;
    this.timer = setInterval(() => {
      this.probeAll().catch(() => {});
    }, this.intervalMs);

    // Initial probe unref'd
    if (this.timer.unref) this.timer.unref();
  }

  public stop(): void {
    if (this.timer) {
      clearInterval(this.timer);
      this.timer = null;
    }
  }

  public async probeAll(): Promise<Record<string, ProviderHealth>> {
    const results: Record<string, ProviderHealth> = {};
    const adapters = this.providerRegistry.getAllAdapters();

    await Promise.all(
      adapters.map(async (adapter) => {
        try {
          const health = await this.providerRegistry.checkHealth(adapter.id);
          results[adapter.id] = health;

          const previousStatus = this.lastHealth.get(adapter.id);
          if (previousStatus && previousStatus !== health.status) {
            this.eventBus.publish('provider_health_change', {
              provider_id: adapter.id,
              channel: adapter.channel,
              old_status: previousStatus,
              new_status: health.status,
              latency_ms: health.latency_ms,
              timestamp: Date.now(),
            });
          }
          this.lastHealth.set(adapter.id, health.status);

          if (health.status === 'down' && this.circuitBreaker) {
            this.circuitBreaker.recordFailure(adapter.id);
          } else if (
            health.status === 'healthy' &&
            this.circuitBreaker &&
            this.circuitBreaker.getState(adapter.id) !== 'CLOSED'
          ) {
            this.circuitBreaker.recordSuccess(adapter.id, health.latency_ms);
          }
        } catch {
          // Handled
        }
      })
    );

    return results;
  }
}
