/**
 * In-Memory Health Event Publisher
 *
 * Tracks health-related events for testing purposes.
 * This publisher allows verification of event emission patterns
 * without requiring external event bus infrastructure.
 */

import {
  HealthEventPublisher,
  HealthCheckCompletedEvent,
  HealthCheckFailedEvent,
  HealthCheckTimeoutEvent,
  ConnectedEvent,
  DisconnectedEvent,
  DegradedEvent,
  CheckingEvent,
} from '../../../core/health/ports/HealthEventPublisher';

export interface HealthCheckCompletedEventWithType {
  type: 'HealthCheckCompleted';
  healthy: boolean;
  responseTime: number;
  timestamp: Date;
  endpoint?: string;
}

export interface HealthCheckFailedEventWithType {
  type: 'HealthCheckFailed';
  error: string;
  timestamp: Date;
  endpoint?: string;
}

export interface HealthCheckTimeoutEventWithType {
  type: 'HealthCheckTimeout';
  timestamp: Date;
  endpoint?: string;
}

export interface ConnectedEventWithType {
  type: 'Connected';
  timestamp: Date;
  responseTime: number;
}

export interface DisconnectedEventWithType {
  type: 'Disconnected';
  timestamp: Date;
  consecutiveFailures: number;
}

export interface DegradedEventWithType {
  type: 'Degraded';
  timestamp: Date;
  reliability: number;
}

export interface CheckingEventWithType {
  type: 'Checking';
  timestamp: Date;
}

export type HealthEvent =
  | HealthCheckCompletedEventWithType
  | HealthCheckFailedEventWithType
  | HealthCheckTimeoutEventWithType
  | ConnectedEventWithType
  | DisconnectedEventWithType
  | DegradedEventWithType
  | CheckingEventWithType;

export class InMemoryHealthEventPublisher implements HealthEventPublisher {
  private events: HealthEvent[] = [];
  private shouldFail: boolean = false;

  /**
   * Publish a health check completed event
   */
  async publishHealthCheckCompleted(event: HealthCheckCompletedEvent): Promise<void> {
    if (this.shouldFail) throw new Error('Event publisher failed');
    this.events.push({ type: 'HealthCheckCompleted', ...event });
  }

  /**
   * Publish a health check failed event
   */
  async publishHealthCheckFailed(event: HealthCheckFailedEvent): Promise<void> {
    if (this.shouldFail) throw new Error('Event publisher failed');
    this.events.push({ type: 'HealthCheckFailed', ...event });
  }

  /**
   * Publish a health check timeout event
   */
  async publishHealthCheckTimeout(event: HealthCheckTimeoutEvent): Promise<void> {
    if (this.shouldFail) throw new Error('Event publisher failed');
    this.events.push({ type: 'HealthCheckTimeout', ...event });
  }

  /**
   * Publish a connected event
   */
  async publishConnected(event: ConnectedEvent): Promise<void> {
    if (this.shouldFail) throw new Error('Event publisher failed');
    this.events.push({ type: 'Connected', ...event });
  }

  /**
   * Publish a disconnected event
   */
  async publishDisconnected(event: DisconnectedEvent): Promise<void> {
    if (this.shouldFail) throw new Error('Event publisher failed');
    this.events.push({ type: 'Disconnected', ...event });
  }

  /**
   * Publish a degraded event
   */
  async publishDegraded(event: DegradedEvent): Promise<void> {
    if (this.shouldFail) throw new Error('Event publisher failed');
    this.events.push({ type: 'Degraded', ...event });
  }

  /**
   * Publish a checking event
   */
  async publishChecking(event: CheckingEvent): Promise<void> {
    if (this.shouldFail) throw new Error('Event publisher failed');
    this.events.push({ type: 'Checking', ...event });
  }

  /**
   * Get all published events
   */
  getEvents(): HealthEvent[] {
    return [...this.events];
  }

  /**
   * Get events by type
   */
  getEventsByType<T extends HealthEvent['type']>(type: T): Extract<HealthEvent, { type: T }>[] {
    return this.events.filter((event): event is Extract<HealthEvent, { type: T }> => event.type === type);
  }

  /**
   * Get the count of events
   */
  getEventCount(): number {
    return this.events.length;
  }

  /**
   * Get the count of events by type
   */
  getEventCountByType(type: HealthEvent['type']): number {
    return this.events.filter(event => event.type === type).length;
  }

  /**
   * Clear all published events
   */
  clear(): void {
    this.events = [];
    this.shouldFail = false;
  }

  /**
   * Configure the publisher to fail on publish
   */
  setShouldFail(shouldFail: boolean): void {
    this.shouldFail = shouldFail;
  }
}
