import { describe, it, expect, beforeEach, vi } from 'vitest';
import { ApiConnectionMonitor } from './ApiConnectionMonitor';

describe('ApiConnectionMonitor', () => {
  let monitor: ApiConnectionMonitor;

  beforeEach(() => {
    // Reset singleton instance
    (ApiConnectionMonitor as any).instance = undefined;
    monitor = ApiConnectionMonitor.getInstance();
  });

  describe('getInstance', () => {
    it('should return a singleton instance', () => {
      const instance1 = ApiConnectionMonitor.getInstance();
      const instance2 = ApiConnectionMonitor.getInstance();
      expect(instance1).toBe(instance2);
    });
  });

  describe('startMonitoring', () => {
    it('should start monitoring without errors', () => {
      expect(() => monitor.startMonitoring()).not.toThrow();
    });

    it('should be idempotent', () => {
      monitor.startMonitoring();
      expect(() => monitor.startMonitoring()).not.toThrow();
    });
  });

  describe('recordSuccess', () => {
    it('should record a successful request', () => {
      const responseTime = 100;
      monitor.recordSuccess(responseTime);
      
      const health = monitor.getHealth();
      expect(health.totalRequests).toBe(1);
      expect(health.successfulRequests).toBe(1);
      expect(health.failedRequests).toBe(0);
    });

    it('should update average response time', () => {
      monitor.recordSuccess(100);
      monitor.recordSuccess(200);
      
      const health = monitor.getHealth();
      expect(health.averageResponseTime).toBe(150);
    });
  });

  describe('recordFailure', () => {
    it('should record a failed request', () => {
      const error = new Error('Test error');
      monitor.recordFailure(error);
      
      const health = monitor.getHealth();
      expect(health.totalRequests).toBe(1);
      expect(health.successfulRequests).toBe(0);
      expect(health.failedRequests).toBe(1);
    });

    it('should track consecutive failures', () => {
      const error1 = new Error('Error 1');
      const error2 = new Error('Error 2');
      
      monitor.recordFailure(error1);
      monitor.recordFailure(error2);
      
      const health = monitor.getHealth();
      expect(health.consecutiveFailures).toBe(2);
    });
  });

  describe('getStatus', () => {
    it('should return current status', () => {
      const status = monitor.getStatus();
      expect(typeof status).toBe('string');
      expect(['connected', 'disconnected', 'degraded', 'checking']).toContain(status);
    });
  });

  describe('getHealth', () => {
    it('should return health metrics', () => {
      const health = monitor.getHealth();
      expect(health).toHaveProperty('status');
      expect(health).toHaveProperty('lastCheck');
      expect(health).toHaveProperty('lastSuccess');
      expect(health).toHaveProperty('lastFailure');
      expect(health).toHaveProperty('consecutiveFailures');
      expect(health).toHaveProperty('totalRequests');
      expect(health).toHaveProperty('successfulRequests');
      expect(health).toHaveProperty('failedRequests');
      expect(health).toHaveProperty('averageResponseTime');
    });

    it('should calculate success rate correctly', () => {
      monitor.recordSuccess(100);
      monitor.recordSuccess(100);
      monitor.recordFailure(new Error('Test'));
      
      const health = monitor.getHealth();
      const successRate = health.successfulRequests / health.totalRequests;
      expect(successRate).toBeCloseTo(2/3, 10);
    });
  });

  describe('isAvailable', () => {
    it('should return true when healthy', () => {
      // Record some successful requests
      for (let i = 0; i < 5; i++) {
        monitor.recordSuccess(100);
      }
      
      expect(monitor.isAvailable()).toBe(true);
    });

    it('should return false when many failures occur', () => {
      // Record many failures
      for (let i = 0; i < 10; i++) {
        monitor.recordFailure(new Error('Test'));
      }

      expect(monitor.isAvailable()).toBe(false);
    });
  });

  describe('getReliability', () => {
    it('should return reliability score', () => {
      monitor.recordSuccess(100);
      monitor.recordSuccess(100);
      monitor.recordSuccess(100);
      monitor.recordFailure(new Error('Test'));
      
      const reliability = monitor.getReliability();
      expect(reliability).toBeGreaterThanOrEqual(0);
      expect(reliability).toBeLessThanOrEqual(100);
    });

    it('should return 1 for perfect reliability', () => {
      for (let i = 0; i < 10; i++) {
        monitor.recordSuccess(100);
      }

      expect(monitor.getReliability()).toBe(100);
    });

    it('should return 0 for complete failure', () => {
      for (let i = 0; i < 10; i++) {
        monitor.recordFailure(new Error('Test'));
      }
      
      expect(monitor.getReliability()).toBe(0);
    });
  });

  describe('performHealthCheck', () => {
    it('should return health check result', async () => {
      const result = await monitor.performHealthCheck();
      expect(result).toHaveProperty('timestamp');
      expect(result).toHaveProperty('healthy');
      expect(result).toHaveProperty('responseTime');
    });
  });
});
