/**
 * Integration Test: Health Check Use Case Orchestration
 *
 * Tests the orchestration logic of health check-related Use Cases:
 * - CheckApiHealthUseCase: Executes health checks and returns status
 * - GetConnectionStatusUseCase: Retrieves current connection status
 * - Validates that Use Cases correctly interact with their Ports (Health Check Adapter, Event Publisher)
 * - Uses In-Memory adapters for fast, deterministic testing
 *
 * Focus: Business logic orchestration, NOT UI rendering
 */

import { describe, it, expect, beforeAll, beforeEach } from 'vitest';
import { InMemoryHealthCheckAdapter } from '../../../adapters/health/persistence/inmemory/InMemoryHealthCheckAdapter';
import { InMemoryHealthEventPublisher } from '../../../adapters/events/InMemoryHealthEventPublisher';
import { CheckApiHealthUseCase } from '../../../core/health/use-cases/CheckApiHealthUseCase';
import { GetConnectionStatusUseCase } from '../../../core/health/use-cases/GetConnectionStatusUseCase';

describe('Health Check Use Case Orchestration', () => {
  let healthCheckAdapter: InMemoryHealthCheckAdapter;
  let eventPublisher: InMemoryHealthEventPublisher;
  let checkApiHealthUseCase: CheckApiHealthUseCase;
  let getConnectionStatusUseCase: GetConnectionStatusUseCase;

  beforeAll(() => {
    // Initialize In-Memory adapters and event publisher
    healthCheckAdapter = new InMemoryHealthCheckAdapter();
    eventPublisher = new InMemoryHealthEventPublisher();
    checkApiHealthUseCase = new CheckApiHealthUseCase({
      healthCheckAdapter,
      eventPublisher,
    });
    getConnectionStatusUseCase = new GetConnectionStatusUseCase({
      healthCheckAdapter,
    });
  });

  beforeEach(() => {
    // Clear all In-Memory repositories before each test
    healthCheckAdapter.clear();
    eventPublisher.clear();
  });

  describe('CheckApiHealthUseCase - Success Path', () => {
    it('should perform health check and return healthy status', async () => {
      // Scenario: API is healthy and responsive
      // Given: HealthCheckAdapter returns successful response
      // And: Response time is 50ms
      healthCheckAdapter.setResponseTime(50);

      // When: CheckApiHealthUseCase.execute() is called
      const result = await checkApiHealthUseCase.execute();

      // Then: Result should show healthy=true
      expect(result.healthy).toBe(true);
      
      // And: Response time should be 50ms
      expect(result.responseTime).toBeGreaterThanOrEqual(50);
      
      // And: Timestamp should be present
      expect(result.timestamp).toBeInstanceOf(Date);
      
      // And: EventPublisher should emit HealthCheckCompletedEvent
      expect(eventPublisher.getEventCountByType('HealthCheckCompleted')).toBe(1);
    });

    it('should perform health check with slow response time', async () => {
      // Scenario: API is healthy but slow
      // Given: HealthCheckAdapter returns successful response
      // And: Response time is 500ms
      healthCheckAdapter.setResponseTime(500);

      // When: CheckApiHealthUseCase.execute() is called
      const result = await checkApiHealthUseCase.execute();

      // Then: Result should show healthy=true
      expect(result.healthy).toBe(true);
      
      // And: Response time should be 500ms
      expect(result.responseTime).toBeGreaterThanOrEqual(500);
      
      // And: EventPublisher should emit HealthCheckCompletedEvent
      expect(eventPublisher.getEventCountByType('HealthCheckCompleted')).toBe(1);
    });

    it('should handle health check with custom endpoint', async () => {
      // Scenario: Health check on custom endpoint
      // Given: HealthCheckAdapter returns success for /custom/health
      healthCheckAdapter.configureResponse('/custom/health', {
        healthy: true,
        responseTime: 50,
        timestamp: new Date(),
      });

      // When: CheckApiHealthUseCase.execute() is called
      const result = await checkApiHealthUseCase.execute();

      // Then: Result should show healthy=true
      expect(result.healthy).toBe(true);
      
      // And: EventPublisher should emit HealthCheckCompletedEvent
      expect(eventPublisher.getEventCountByType('HealthCheckCompleted')).toBe(1);
    });
  });

  describe('CheckApiHealthUseCase - Failure Path', () => {
    it('should handle failed health check and return unhealthy status', async () => {
      // Scenario: API is unreachable
      // Given: HealthCheckAdapter throws network error
      healthCheckAdapter.setShouldFail(true, 'ECONNREFUSED');

      // When: CheckApiHealthUseCase.execute() is called
      const result = await checkApiHealthUseCase.execute();

      // Then: Result should show healthy=false
      expect(result.healthy).toBe(false);
      
      // And: Error message should be present
      expect(result.error).toBeDefined();
      
      // And: EventPublisher should emit HealthCheckFailedEvent
      expect(eventPublisher.getEventCountByType('HealthCheckFailed')).toBe(1);
    });

    it('should handle timeout during health check', async () => {
      // Scenario: Health check times out
      // Given: HealthCheckAdapter times out after 30 seconds
      healthCheckAdapter.setShouldFail(true, 'Timeout');

      // When: CheckApiHealthUseCase.execute() is called
      const result = await checkApiHealthUseCase.execute();

      // Then: Result should show healthy=false
      expect(result.healthy).toBe(false);
      
      // And: Error should indicate timeout
      expect(result.error).toContain('Timeout');
      
      // And: EventPublisher should emit HealthCheckTimeoutEvent
      expect(eventPublisher.getEventCountByType('HealthCheckTimeout')).toBe(1);
    });

    it('should handle malformed response from health endpoint', async () => {
      // Scenario: Health endpoint returns invalid JSON
      // Given: HealthCheckAdapter returns malformed response
      healthCheckAdapter.setShouldFail(true, 'Invalid JSON');

      // When: CheckApiHealthUseCase.execute() is called
      const result = await checkApiHealthUseCase.execute();

      // Then: Result should show healthy=false
      expect(result.healthy).toBe(false);
      
      // And: Error should indicate parsing failure
      expect(result.error).toContain('Invalid JSON');
      
      // And: EventPublisher should emit HealthCheckFailedEvent
      expect(eventPublisher.getEventCountByType('HealthCheckFailed')).toBe(1);
    });
  });

  describe('GetConnectionStatusUseCase - Success Path', () => {
    it('should retrieve connection status when healthy', async () => {
      // Scenario: Connection is healthy
      // Given: HealthCheckAdapter has successful checks
      // And: Connection status is 'connected'
      healthCheckAdapter.setResponseTime(50);

      // Perform successful health check
      await checkApiHealthUseCase.execute();

      // When: GetConnectionStatusUseCase.execute() is called
      const result = await getConnectionStatusUseCase.execute();

      // Then: Result should show status='connected'
      expect(result.status).toBe('connected');
      
      // And: Reliability should be 100%
      expect(result.reliability).toBe(100);
      
      // And: Last check timestamp should be present
      expect(result.lastCheck).toBeInstanceOf(Date);
    });

    it('should retrieve connection status when degraded', async () => {
      // Scenario: Connection is degraded
      // Given: HealthCheckAdapter has mixed results (5 success, 3 fail)
      // And: Connection status is 'degraded'
      healthCheckAdapter.setResponseTime(50);

      // Perform 5 successful checks
      for (let i = 0; i < 5; i++) {
        await checkApiHealthUseCase.execute();
      }

      // Now start failing
      healthCheckAdapter.setShouldFail(true, 'ECONNREFUSED');

      // Perform 3 failed checks
      for (let i = 0; i < 3; i++) {
        await checkApiHealthUseCase.execute();
      }

      // When: GetConnectionStatusUseCase.execute() is called
      const result = await getConnectionStatusUseCase.execute();

      // Then: Result should show status='degraded'
      expect(result.status).toBe('degraded');
      
      // And: Reliability should be 62.5%
      expect(result.reliability).toBeCloseTo(62.5, 1);
      
      // And: Consecutive failures should be 0
      expect(result.consecutiveFailures).toBe(0);
    });

    it('should retrieve connection status when disconnected', async () => {
      // Scenario: Connection is disconnected
      // Given: HealthCheckAdapter has 3 consecutive failures
      // And: Connection status is 'disconnected'
      healthCheckAdapter.setShouldFail(true, 'ECONNREFUSED');

      // Perform 3 failed checks
      for (let i = 0; i < 3; i++) {
        await checkApiHealthUseCase.execute();
      }

      // When: GetConnectionStatusUseCase.execute() is called
      const result = await getConnectionStatusUseCase.execute();

      // Then: Result should show status='disconnected'
      expect(result.status).toBe('disconnected');
      
      // And: Consecutive failures should be 3
      expect(result.consecutiveFailures).toBe(3);
      
      // And: Last failure timestamp should be present
      expect(result.lastFailure).toBeInstanceOf(Date);
    });

    it('should retrieve connection status when checking', async () => {
      // Scenario: Connection status is checking
      // Given: No health checks performed yet
      // And: Connection status is 'checking'
      // When: GetConnectionStatusUseCase.execute() is called
      const result = await getConnectionStatusUseCase.execute();

      // Then: Result should show status='checking'
      expect(result.status).toBe('checking');
      
      // And: Reliability should be 0
      expect(result.reliability).toBe(0);
    });
  });

  describe('GetConnectionStatusUseCase - Metrics', () => {
    it('should calculate reliability correctly', async () => {
      // Scenario: Calculate reliability from mixed results
      // Given: 7 successful requests and 3 failed requests
      healthCheckAdapter.setResponseTime(50);

      // Perform 7 successful checks
      for (let i = 0; i < 7; i++) {
        await checkApiHealthUseCase.execute();
      }

      // Now start failing
      healthCheckAdapter.setShouldFail(true, 'ECONNREFUSED');

      // Perform 3 failed checks
      for (let i = 0; i < 3; i++) {
        await checkApiHealthUseCase.execute();
      }

      // When: GetConnectionStatusUseCase.execute() is called
      const result = await getConnectionStatusUseCase.execute();

      // Then: Result should show reliability=70%
      expect(result.reliability).toBeCloseTo(70, 1);
      
      // And: Total requests should be 10
      expect(result.totalRequests).toBe(10);
      
      // And: Successful requests should be 7
      expect(result.successfulRequests).toBe(7);
      
      // And: Failed requests should be 3
      expect(result.failedRequests).toBe(3);
    });

    it('should calculate average response time correctly', async () => {
      // Scenario: Calculate average from varying response times
      // Given: Response times of 50ms, 100ms, 150ms
      const responseTimes = [50, 100, 150];
      
      // Mock different response times
      let callCount = 0;
      const originalPerformHealthCheck = healthCheckAdapter.performHealthCheck.bind(healthCheckAdapter);
      healthCheckAdapter.performHealthCheck = async () => {
        const time = responseTimes[callCount] || 50;
        callCount++;
        await new Promise(resolve => setTimeout(resolve, time));
        return {
          healthy: true,
          responseTime: time,
          timestamp: new Date(),
        };
      };

      // Perform 3 health checks
      await checkApiHealthUseCase.execute();
      await checkApiHealthUseCase.execute();
      await checkApiHealthUseCase.execute();

      // When: GetConnectionStatusUseCase.execute() is called
      const result = await getConnectionStatusUseCase.execute();

      // Then: Result should show averageResponseTime=100ms
      expect(result.averageResponseTime).toBeCloseTo(100, 1);
    });

    it('should handle zero requests for metrics calculation', async () => {
      // Scenario: No requests made yet
      // Given: No health checks performed
      // When: GetConnectionStatusUseCase.execute() is called
      const result = await getConnectionStatusUseCase.execute();

      // Then: Result should show reliability=0
      expect(result.reliability).toBe(0);
      
      // And: Average response time should be 0
      expect(result.averageResponseTime).toBe(0);
      
      // And: Total requests should be 0
      expect(result.totalRequests).toBe(0);
    });
  });

  describe('Health Check Data Orchestration', () => {
    it('should correctly format health check result with all fields', async () => {
      // Scenario: Complete health check result
      // Given: HealthCheckAdapter returns successful response
      // And: Response time is 75ms
      healthCheckAdapter.setResponseTime(75);

      // When: CheckApiHealthUseCase.execute() is called
      const result = await checkApiHealthUseCase.execute();

      // Then: Result should contain:
      expect(result.healthy).toBe(true);
      expect(result.responseTime).toBeGreaterThanOrEqual(75);
      expect(result.timestamp).toBeInstanceOf(Date);
      expect(result.error).toBeUndefined();
    });

    it('should correctly format connection status with all fields', async () => {
      // Scenario: Complete connection status
      // Given: HealthCheckAdapter has 5 success, 3 fail
      healthCheckAdapter.setResponseTime(50);

      // Perform 5 successful checks
      for (let i = 0; i < 5; i++) {
        await checkApiHealthUseCase.execute();
      }

      // Now start failing
      healthCheckAdapter.setShouldFail(true, 'ECONNREFUSED');

      // Perform 3 failed checks
      for (let i = 0; i < 3; i++) {
        await checkApiHealthUseCase.execute();
      }

      // When: GetConnectionStatusUseCase.execute() is called
      const result = await getConnectionStatusUseCase.execute();

      // Then: Result should contain:
      expect(result.status).toBe('degraded');
      expect(result.reliability).toBeCloseTo(62.5, 1);
      expect(result.totalRequests).toBe(8);
      expect(result.successfulRequests).toBe(5);
      expect(result.failedRequests).toBe(3);
      expect(result.consecutiveFailures).toBe(0);
      expect(result.averageResponseTime).toBeGreaterThanOrEqual(50);
      expect(result.lastCheck).toBeInstanceOf(Date);
      expect(result.lastSuccess).toBeInstanceOf(Date);
      expect(result.lastFailure).toBeInstanceOf(Date);
    });

    it('should correctly format connection status when disconnected', async () => {
      // Scenario: Connection is disconnected
      // Given: HealthCheckAdapter has 3 consecutive failures
      healthCheckAdapter.setShouldFail(true, 'ECONNREFUSED');

      // Perform 3 failed checks
      for (let i = 0; i < 3; i++) {
        await checkApiHealthUseCase.execute();
      }

      // When: GetConnectionStatusUseCase.execute() is called
      const result = await getConnectionStatusUseCase.execute();

      // Then: Result should contain:
      expect(result.status).toBe('disconnected');
      expect(result.consecutiveFailures).toBe(3);
      expect(result.lastFailure).toBeInstanceOf(Date);
      expect(result.lastSuccess).toBeInstanceOf(Date);
    });
  });

  describe('Event Emission Patterns', () => {
    it('should emit HealthCheckCompletedEvent on successful check', async () => {
      // Scenario: Successful health check
      // Given: HealthCheckAdapter returns success
      healthCheckAdapter.setResponseTime(50);

      // When: CheckApiHealthUseCase.execute() is called
      await checkApiHealthUseCase.execute();

      // Then: EventPublisher should emit HealthCheckCompletedEvent
      expect(eventPublisher.getEventCountByType('HealthCheckCompleted')).toBe(1);
      
      // And: Event should include health check result
      const events = eventPublisher.getEventsByType('HealthCheckCompleted');
      expect(events[0].healthy).toBe(true);
      expect(events[0].responseTime).toBeGreaterThanOrEqual(50);
      expect(events[0].timestamp).toBeInstanceOf(Date);
    });

    it('should emit HealthCheckFailedEvent on failed check', async () => {
      // Scenario: Failed health check
      // Given: HealthCheckAdapter throws error
      healthCheckAdapter.setShouldFail(true, 'ECONNREFUSED');

      // When: CheckApiHealthUseCase.execute() is called
      await checkApiHealthUseCase.execute();

      // Then: EventPublisher should emit HealthCheckFailedEvent
      expect(eventPublisher.getEventCountByType('HealthCheckFailed')).toBe(1);
      
      // And: Event should include error details
      const events = eventPublisher.getEventsByType('HealthCheckFailed');
      expect(events[0].error).toBe('ECONNREFUSED');
      expect(events[0].timestamp).toBeInstanceOf(Date);
    });

    it('should emit ConnectionStatusChangedEvent on status change', async () => {
      // Scenario: Connection status changes
      // Given: Current status is 'disconnected'
      // And: HealthCheckAdapter returns success
      healthCheckAdapter.setShouldFail(true, 'ECONNREFUSED');

      // Perform 3 failed checks to get disconnected status
      for (let i = 0; i < 3; i++) {
        await checkApiHealthUseCase.execute();
      }

      // Now start succeeding
      healthCheckAdapter.setShouldFail(false);
      healthCheckAdapter.setResponseTime(50);

      // When: CheckApiHealthUseCase.execute() is called
      await checkApiHealthUseCase.execute();

      // Then: EventPublisher should emit ConnectedEvent
      expect(eventPublisher.getEventCountByType('Connected')).toBe(1);
      
      // And: Event should include timestamp and response time
      const events = eventPublisher.getEventsByType('Connected');
      expect(events[0].timestamp).toBeInstanceOf(Date);
      expect(events[0].responseTime).toBeGreaterThanOrEqual(50);
    });
  });

  describe('Error Handling', () => {
    it('should handle adapter errors gracefully', async () => {
      // Scenario: HealthCheckAdapter throws unexpected error
      // Given: HealthCheckAdapter throws generic error
      healthCheckAdapter.setShouldFail(true, 'Unexpected error');

      // When: CheckApiHealthUseCase.execute() is called
      const result = await checkApiHealthUseCase.execute();

      // Then: Should not throw unhandled error
      expect(result).toBeDefined();
      
      // And: Should return unhealthy status
      expect(result.healthy).toBe(false);
      
      // And: Should include error message
      expect(result.error).toBe('Unexpected error');
    });

    it('should handle invalid endpoint configuration', async () => {
      // Scenario: Invalid endpoint provided
      // Given: Invalid endpoint string
      healthCheckAdapter.setShouldFail(true, 'Invalid endpoint');

      // When: CheckApiHealthUseCase.execute() is called
      const result = await checkApiHealthUseCase.execute();

      // Then: Should handle validation error
      expect(result).toBeDefined();
      
      // And: Should return error status
      expect(result.healthy).toBe(false);
      expect(result.error).toBe('Invalid endpoint');
    });

    it('should handle concurrent health check calls', async () => {
      // Scenario: Multiple simultaneous health checks
      // Given: CheckApiHealthUseCase.execute() is already running
      let resolveFirst: (value: any) => void;
      const firstPromise = new Promise<any>((resolve) => {
        resolveFirst = resolve;
      });

      const originalPerformHealthCheck = healthCheckAdapter.performHealthCheck.bind(healthCheckAdapter);
      healthCheckAdapter.performHealthCheck = async () => firstPromise;

      // Start first health check
      const firstCheck = checkApiHealthUseCase.execute();

      // When: CheckApiHealthUseCase.execute() is called again
      const secondCheck = checkApiHealthUseCase.execute();

      // Resolve the first check
      resolveFirst!({
        healthy: true,
        responseTime: 50,
        timestamp: new Date(),
      });

      // Wait for both checks to complete
      const [result1, result2] = await Promise.all([firstCheck, secondCheck]);

      // Then: Should return existing result
      expect(result1.healthy).toBe(true);
      expect(result2.healthy).toBe(true);
    });
  });
});