/**
 * Docker Manager for Integration Tests
 * Manages Docker Compose services for integration testing
 */

import { execSync, spawn } from 'child_process';
import { setTimeout } from 'timers/promises';

export interface DockerServiceConfig {
  name: string;
  port: number;
  healthCheck: string;
  timeout?: number;
}

export class DockerManager {
  private static instance: DockerManager;
  private services: Map<string, boolean> = new Map();
  private composeProject = 'gridpilot-test';
  private composeFile = 'docker-compose.test.yml';

  private constructor() {}

  static getInstance(): DockerManager {
    if (!DockerManager.instance) {
      DockerManager.instance = new DockerManager();
    }
    return DockerManager.instance;
  }

  /**
   * Check if Docker services are already running
   */
  isRunning(): boolean {
    try {
      const output = execSync(
        `docker-compose -p ${this.composeProject} -f ${this.composeFile} ps -q 2>/dev/null || true`,
        { encoding: 'utf8' }
      ).trim();
      return output.length > 0;
    } catch {
      return false;
    }
  }

  /**
   * Start Docker services with dependency checking
   */
  async start(): Promise<void> {
    console.log('[DockerManager] Starting test environment...');

    if (this.isRunning()) {
      console.log('[DockerManager] Services already running, checking health...');
      await this.waitForServices();
      return;
    }

    // Start services
    execSync(
      `COMPOSE_PARALLEL_LIMIT=1 docker-compose -p ${this.composeProject} -f ${this.composeFile} up -d ready api`,
      { stdio: 'inherit' }
    );

    console.log('[DockerManager] Services starting, waiting for health...');
    await this.waitForServices();
  }

  /**
   * Wait for all services to be healthy using polling
   */
  async waitForServices(): Promise<void> {
    const services: DockerServiceConfig[] = [
      {
        name: 'db',
        port: 5433,
        healthCheck: 'pg_isready -U gridpilot_test_user -d gridpilot_test',
        timeout: 60000
      },
      {
        name: 'api',
        port: 3101,
        healthCheck: 'curl -f http://localhost:3101/health',
        timeout: 90000
      }
    ];

    for (const service of services) {
      await this.waitForService(service);
    }
  }

  /**
   * Wait for a single service to be healthy
   */
  async waitForService(config: DockerServiceConfig): Promise<void> {
    const timeout = config.timeout || 30000;
    const startTime = Date.now();

    console.log(`[DockerManager] Waiting for ${config.name}...`);

    while (Date.now() - startTime < timeout) {
      try {
        // Try health check command
        if (config.name === 'db') {
          // For DB, check if it's ready to accept connections
          try {
            execSync(
              `docker exec ${this.composeProject}-${config.name}-1 ${config.healthCheck} 2>/dev/null`,
              { stdio: 'pipe' }
            );
            console.log(`[DockerManager] ✓ ${config.name} is healthy`);
            return;
          } catch {}
        } else {
          // For API, check HTTP endpoint
          const response = await fetch(`http://localhost:${config.port}/health`);
          if (response.ok) {
            console.log(`[DockerManager] ✓ ${config.name} is healthy`);
            return;
          }
        }
      } catch (error) {
        // Service not ready yet, continue waiting
      }

      await setTimeout(1000);
    }

    throw new Error(`[DockerManager] ${config.name} failed to become healthy within ${timeout}ms`);
  }

  /**
   * Stop Docker services
   */
  stop(): void {
    console.log('[DockerManager] Stopping test environment...');
    try {
      execSync(
        `docker-compose -p ${this.composeProject} -f ${this.composeFile} down --remove-orphans`,
        { stdio: 'inherit' }
      );
    } catch (error) {
      console.warn('[DockerManager] Warning: Failed to stop services cleanly:', error);
    }
  }

  /**
   * Clean up volumes and containers
   */
  clean(): void {
    console.log('[DockerManager] Cleaning up test environment...');
    try {
      execSync(
        `docker-compose -p ${this.composeProject} -f ${this.composeFile} down -v --remove-orphans --volumes`,
        { stdio: 'inherit' }
      );
    } catch (error) {
      console.warn('[DockerManager] Warning: Failed to clean up cleanly:', error);
    }
  }

  /**
   * Execute a command in a service container
   */
  execInService(service: string, command: string): string {
    try {
      return execSync(
        `docker exec ${this.composeProject}-${service}-1 ${command}`,
        { encoding: 'utf8' }
      );
    } catch (error) {
      throw new Error(`Failed to execute command in ${service}: ${error}`);
    }
  }

  /**
   * Get service logs
   */
  getLogs(service: string): string {
    try {
      return execSync(
        `docker logs ${this.composeProject}-${service}-1 --tail 100`,
        { encoding: 'utf8' }
      );
    } catch (error) {
      return `Failed to get logs: ${error}`;
    }
  }
}