/**
 * API Connection Status Monitor and Health Checks
 */

import { EventEmitter } from 'events';

export type ConnectionStatus = 'connected' | 'disconnected' | 'degraded' | 'checking';

export interface ConnectionHealth {
  status: ConnectionStatus;
  lastCheck: Date | null;
  lastSuccess: Date | null;
  lastFailure: Date | null;
  consecutiveFailures: number;
  totalRequests: number;
  successfulRequests: number;
  failedRequests: number;
  averageResponseTime: number;
}

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

export class ApiConnectionMonitor extends EventEmitter {
  private static instance: ApiConnectionMonitor;
  private health: ConnectionHealth;
  private isChecking = false;
  private checkInterval: NodeJS.Timeout | null = null;
  private healthCheckEndpoint: string;
  private readonly CHECK_INTERVAL = 300000; // 5 minutes
  private readonly DEGRADATION_THRESHOLD = 0.7; // 70% failure rate

  private constructor(healthCheckEndpoint: string = '/health') {
    super();
    this.healthCheckEndpoint = healthCheckEndpoint;
    this.health = {
      status: 'disconnected',
      lastCheck: null,
      lastSuccess: null,
      lastFailure: null,
      consecutiveFailures: 0,
      totalRequests: 0,
      successfulRequests: 0,
      failedRequests: 0,
      averageResponseTime: 0,
    };
  }

  static getInstance(healthCheckEndpoint?: string): ApiConnectionMonitor {
    if (!ApiConnectionMonitor.instance) {
      ApiConnectionMonitor.instance = new ApiConnectionMonitor(healthCheckEndpoint);
    }
    return ApiConnectionMonitor.instance;
  }

  /**
   * Start automatic health monitoring
   */
  startMonitoring(intervalMs?: number): void {
    if (this.checkInterval) {
      clearInterval(this.checkInterval);
    }

    const interval = intervalMs || this.CHECK_INTERVAL;
    this.checkInterval = setInterval(() => {
      this.performHealthCheck();
    }, interval);

    // Initial check
    this.performHealthCheck();
  }

  /**
   * Stop automatic health monitoring
   */
  stopMonitoring(): void {
    if (this.checkInterval) {
      clearInterval(this.checkInterval);
      this.checkInterval = null;
    }
  }

  /**
   * Perform a manual health check
   */
  async performHealthCheck(): Promise<HealthCheckResult> {
    if (this.isChecking) {
      return {
        healthy: false,
        responseTime: 0,
        error: 'Check already in progress',
        timestamp: new Date(),
      };
    }

    this.isChecking = true;
    const startTime = Date.now();

    try {
      // Try multiple endpoints to determine actual connectivity
      const baseUrl = this.getBaseUrl();
      const endpointsToTry = [
        `${baseUrl}${this.healthCheckEndpoint}`,
        `${baseUrl}/api/health`,
        `${baseUrl}/status`,
        baseUrl, // Root endpoint
      ];

      let lastError: Error | null = null;
      let successfulResponse: Response | null = null;

      for (const endpoint of endpointsToTry) {
        try {
          const controller = new AbortController();
          const timeoutId = setTimeout(() => controller.abort(), 3000);

          const response = await fetch(endpoint, {
            method: 'GET',
            signal: controller.signal,
            cache: 'no-store',
            // Add credentials to handle auth
            credentials: 'include',
          });

          clearTimeout(timeoutId);

          // Consider any response (even 404) as connectivity success
          if (response.ok || response.status === 404 || response.status === 401) {
            successfulResponse = response;
            break;
          }
        } catch (endpointError) {
          lastError = endpointError as Error;
          // Try next endpoint
          continue;
        }
      }

      const responseTime = Date.now() - startTime;

      if (successfulResponse) {
        this.recordSuccess(responseTime);
        this.isChecking = false;

        return {
          healthy: true,
          responseTime,
          timestamp: new Date(),
        };
      } else {
        // If we got here, all endpoints failed
        const errorMessage = lastError?.message || 'All endpoints failed to respond';
        this.recordFailure(errorMessage);
        this.isChecking = false;

        return {
          healthy: false,
          responseTime,
          error: errorMessage,
          timestamp: new Date(),
        };
      }
    } catch (error) {
      const responseTime = Date.now() - startTime;
      const errorMessage = error instanceof Error ? error.message : 'Unknown error';
      
      this.recordFailure(errorMessage);
      this.isChecking = false;

      return {
        healthy: false,
        responseTime,
        error: errorMessage,
        timestamp: new Date(),
      };
    }
  }

  /**
   * Record a successful API request
   */
  recordSuccess(responseTime: number = 0): 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();
    this.emit('success', { responseTime });
  }

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

    this.updateStatus();
    this.emit('failure', { 
      error: typeof error === 'string' ? error : error.message,
      consecutiveFailures: this.health.consecutiveFailures 
    });
  }

  /**
   * Get current connection health
   */
  getHealth(): ConnectionHealth {
    return { ...this.health };
  }

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

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

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

  /**
   * Reset all statistics
   */
  reset(): void {
    this.health = {
      status: 'disconnected',
      lastCheck: null,
      lastSuccess: null,
      lastFailure: null,
      consecutiveFailures: 0,
      totalRequests: 0,
      successfulRequests: 0,
      failedRequests: 0,
      averageResponseTime: 0,
    };
    this.emit('reset');
  }

  /**
   * Get detailed status report for development
   */
  getDebugReport(): string {
    const reliability = this.getReliability().toFixed(2);
    const avgTime = this.health.averageResponseTime.toFixed(2);
    
    return `API Connection Status:
  Status: ${this.health.status}
  Reliability: ${reliability}%
  Total Requests: ${this.health.totalRequests}
  Successful: ${this.health.successfulRequests}
  Failed: ${this.health.failedRequests}
  Consecutive Failures: ${this.health.consecutiveFailures}
  Avg Response Time: ${avgTime}ms
  Last Check: ${this.health.lastCheck?.toISOString() || 'never'}
  Last Success: ${this.health.lastSuccess?.toISOString() || 'never'}
  Last Failure: ${this.health.lastFailure?.toISOString() || 'never'}`;
  }

  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 < this.DEGRADATION_THRESHOLD && this.health.totalRequests >= 5) {
      // Only degrade if we have enough samples and reliability is low
      this.health.status = 'degraded';
    } else if (reliability >= this.DEGRADATION_THRESHOLD || 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';
    }

    // Emit status change events (only on actual changes)
    if (this.health.status === 'disconnected') {
      this.emit('disconnected');
    } else if (this.health.status === 'degraded') {
      this.emit('degraded');
    } else if (this.health.status === 'connected') {
      this.emit('connected');
    } else if (this.health.status === 'checking') {
      this.emit('checking');
    }
  }

  private getBaseUrl(): string {
    // Try to get base URL from environment or fallback
    if (typeof window !== 'undefined') {
      return process.env.NEXT_PUBLIC_API_BASE_URL || 'http://localhost:3001';
    }
    return process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_BASE_URL || 'http://localhost:3001';
  }
}

/**
 * Global connection status utility
 */
export const connectionMonitor = ApiConnectionMonitor.getInstance();

/**
 * Hook for React components to monitor connection status
 */
export function useConnectionStatus() {
  const monitor = ApiConnectionMonitor.getInstance();
  
  return {
    status: monitor.getStatus(),
    health: monitor.getHealth(),
    isAvailable: monitor.isAvailable(),
    reliability: monitor.getReliability(),
    checkHealth: () => monitor.performHealthCheck(),
    getDebugReport: () => monitor.getDebugReport(),
  };
}