/**
 * In-Memory Health Check Adapter
 *
 * Simulates API health check responses for testing purposes.
 * This adapter allows controlled testing of health check scenarios
 * without making actual HTTP requests.
 */

import {
  HealthCheckQuery,
  ConnectionStatus,
  ConnectionHealth,
  HealthCheckResult,
} from '../../../../core/health/ports/HealthCheckQuery';

export interface HealthCheckResponse {
  healthy: boolean;
  responseTime: number;
  error?: string;
  timestamp: Date;
}

export class InMemoryHealthCheckAdapter implements HealthCheckQuery {
  private responses: Map<string, HealthCheckResponse> = new Map();
  public shouldFail: boolean = false;
  public failError: string = 'Network error';
  private responseTime: number = 50;
  private health: ConnectionHealth = {
    status: 'disconnected',
    lastCheck: null,
    lastSuccess: null,
    lastFailure: null,
    consecutiveFailures: 0,
    totalRequests: 0,
    successfulRequests: 0,
    failedRequests: 0,
    averageResponseTime: 0,
  };

  /**
   * Configure the adapter to return a specific response
   */
  configureResponse(endpoint: string, response: HealthCheckResponse): void {
    this.responses.set(endpoint, response);
  }

  /**
   * Configure the adapter to fail all requests
   */
  setShouldFail(shouldFail: boolean, error?: string): void {
    this.shouldFail = shouldFail;
    if (error) {
      this.failError = error;
    }
  }

  /**
   * Set the response time for health checks
   */
  setResponseTime(time: number): void {
    this.responseTime = time;
  }

  /**
   * Perform a health check against an endpoint
   */
  async performHealthCheck(): Promise<HealthCheckResult> {
    // Simulate network delay
    await new Promise(resolve => setTimeout(resolve, this.responseTime));

    if (this.shouldFail) {
      this.recordFailure(this.failError);
      return {
        healthy: false,
        responseTime: this.responseTime,
        error: this.failError,
        timestamp: new Date(),
      };
    }

    // Default successful response
    this.recordSuccess(this.responseTime);
    return {
      healthy: true,
      responseTime: this.responseTime,
      timestamp: new Date(),
    };
  }

  /**
   * Get current connection status
   */
  getStatus(): ConnectionStatus {
    return this.health.status;
  }

  /**
   * Get detailed health information
   */
  getHealth(): ConnectionHealth {
    return { ...this.health };
  }

  /**
   * Get reliability percentage
   */
  getReliability(): number {
    if (this.health.totalRequests === 0) return 0;
    return (this.health.successfulRequests / this.health.totalRequests) * 100;
  }

  /**
   * Check if API is currently available
   */
  isAvailable(): boolean {
    return this.health.status === 'connected' || this.health.status === 'degraded';
  }

  /**
   * Record a successful health check
   */
  private recordSuccess(responseTime: number): void {
    this.health.totalRequests++;
    this.health.successfulRequests++;
    this.health.consecutiveFailures = 0;
    this.health.lastSuccess = new Date();
    this.health.lastCheck = new Date();

    // Update average response time
    const total = this.health.successfulRequests;
    this.health.averageResponseTime =
      ((this.health.averageResponseTime * (total - 1)) + responseTime) / total;

    this.updateStatus();
  }

  /**
   * Record a failed health check
   */
  private recordFailure(error: string): void {
    this.health.totalRequests++;
    this.health.failedRequests++;
    this.health.consecutiveFailures++;
    this.health.lastFailure = new Date();
    this.health.lastCheck = new Date();

    this.updateStatus();
  }

  /**
   * Update connection status based on current metrics
   */
  private updateStatus(): void {
    const reliability = this.health.totalRequests > 0
      ? this.health.successfulRequests / this.health.totalRequests
      : 0;

    // More nuanced status determination
    if (this.health.totalRequests === 0) {
      // No requests yet - don't assume disconnected
      this.health.status = 'checking';
    } else if (this.health.consecutiveFailures >= 3) {
      // Multiple consecutive failures indicates real connectivity issue
      this.health.status = 'disconnected';
    } else if (reliability < 0.7 && this.health.totalRequests >= 5) {
      // Only degrade if we have enough samples and reliability is low
      this.health.status = 'degraded';
    } else if (reliability >= 0.7 || this.health.successfulRequests > 0) {
      // If we have any successes, we're connected
      this.health.status = 'connected';
    } else {
      // Default to checking if uncertain
      this.health.status = 'checking';
    }
  }

  /**
   * Clear all configured responses and settings
   */
  clear(): void {
    this.responses.clear();
    this.shouldFail = false;
    this.failError = 'Network error';
    this.responseTime = 50;
    this.health = {
      status: 'disconnected',
      lastCheck: null,
      lastSuccess: null,
      lastFailure: null,
      consecutiveFailures: 0,
      totalRequests: 0,
      successfulRequests: 0,
      failedRequests: 0,
      averageResponseTime: 0,
    };
  }
}
