/**
 * Integration Test Setup
 * 
 * Sets up test containers and real database instances for integration testing.
 * This file is executed before integration tests run.
 */

import { GenericContainer, StartedTestContainer } from 'testcontainers';
import { MongoMemoryServer } from 'mongodb-memory-server';

let questdbContainer: StartedTestContainer;
let postgresContainer: StartedTestContainer;
let mongoContainer: StartedTestContainer;
let mongoMemoryServer: MongoMemoryServer;

/**
 * Global setup for integration tests
 * Starts real database containers for testing
 */
beforeAll(async () => {
  console.log('🚀 Starting integration test containers...');

  try {
    // Start QuestDB container
    console.log('📊 Starting QuestDB container...');
    questdbContainer = await new GenericContainer('questdb/questdb:7.3.10')
      .withExposedPorts(9000, 8812, 9009)
      .withEnvironment({
        'QDB_TELEMETRY_ENABLED': 'false',
        'QDB_LOG_LEVEL': 'ERROR'
      })
      .withStartupTimeout(60000)
      .start();

    // Start PostgreSQL container
    console.log('🐘 Starting PostgreSQL container...');
    postgresContainer = await new GenericContainer('postgres:15-alpine')
      .withExposedPorts(5432)
      .withEnvironment({
        'POSTGRES_DB': 'trading_bot_test',
        'POSTGRES_USER': 'trading_admin',
        'POSTGRES_PASSWORD': 'trading_pass_test'
      })
      .withStartupTimeout(60000)
      .start();

    // Start MongoDB container
    console.log('🍃 Starting MongoDB container...');
    mongoContainer = await new GenericContainer('mongo:7-jammy')
      .withExposedPorts(27017)
      .withEnvironment({
        'MONGO_INITDB_ROOT_USERNAME': 'trading_admin',
        'MONGO_INITDB_ROOT_PASSWORD': 'trading_mongo_test',
        'MONGO_INITDB_DATABASE': 'trading_bot_test'
      })
      .withStartupTimeout(60000)
      .start();

    // Update environment variables for tests
    process.env.QUESTDB_HOST = questdbContainer.getHost();
    process.env.QUESTDB_HTTP_PORT = questdbContainer.getMappedPort(9000).toString();
    process.env.QUESTDB_PG_PORT = questdbContainer.getMappedPort(8812).toString();
    process.env.QUESTDB_INFLUX_PORT = questdbContainer.getMappedPort(9009).toString();
    
    process.env.POSTGRES_HOST = postgresContainer.getHost();
    process.env.POSTGRES_PORT = postgresContainer.getMappedPort(5432).toString();
    
    process.env.MONGODB_HOST = mongoContainer.getHost();
    process.env.MONGODB_PORT = mongoContainer.getMappedPort(27017).toString();

    console.log('✅ All containers started successfully!');
    console.log(`📊 QuestDB: http://${process.env.QUESTDB_HOST}:${process.env.QUESTDB_HTTP_PORT}`);
    console.log(`🐘 PostgreSQL: ${process.env.POSTGRES_HOST}:${process.env.POSTGRES_PORT}`);
    console.log(`🍃 MongoDB: ${process.env.MONGODB_HOST}:${process.env.MONGODB_PORT}`);

  } catch (error) {
    console.error('❌ Failed to start test containers:', error);
    
    // Try to use MongoDB Memory Server as fallback
    console.log('🔄 Falling back to MongoDB Memory Server...');
    try {
      mongoMemoryServer = await MongoMemoryServer.create({
        instance: {
          dbName: 'trading_bot_test'
        }
      });
      
      const mongoUri = mongoMemoryServer.getUri();
      const mongoUrl = new URL(mongoUri);
      process.env.MONGODB_HOST = mongoUrl.hostname;
      process.env.MONGODB_PORT = mongoUrl.port;
      process.env.MONGODB_URI = mongoUri;
      
      console.log('✅ MongoDB Memory Server started as fallback');
    } catch (fallbackError) {
      console.error('❌ Failed to start MongoDB Memory Server:', fallbackError);
      throw fallbackError;
    }
    
    // For other databases, use localhost defaults if containers fail
    if (!questdbContainer) {
      console.log('⚠️  Using localhost QuestDB (ensure it\'s running)');
      process.env.QUESTDB_HOST = 'localhost';
      process.env.QUESTDB_HTTP_PORT = '9000';
      process.env.QUESTDB_PG_PORT = '8812';
      process.env.QUESTDB_INFLUX_PORT = '9009';
    }
    
    if (!postgresContainer) {
      console.log('⚠️  Using localhost PostgreSQL (ensure it\'s running)');
      process.env.POSTGRES_HOST = 'localhost';
      process.env.POSTGRES_PORT = '5432';
    }
  }
}, 120000); // 2 minutes timeout for container startup

/**
 * Global cleanup for integration tests
 * Stops all test containers
 */
afterAll(async () => {
  console.log('🧹 Cleaning up integration test containers...');

  const cleanup = async (container: StartedTestContainer | undefined, name: string) => {
    if (container) {
      try {
        await container.stop();
        console.log(`✅ ${name} container stopped`);
      } catch (error) {
        console.warn(`⚠️  Failed to stop ${name} container:`, error);
      }
    }
  };

  await Promise.all([
    cleanup(questdbContainer, 'QuestDB'),
    cleanup(postgresContainer, 'PostgreSQL'),
    cleanup(mongoContainer, 'MongoDB')
  ]);

  if (mongoMemoryServer) {
    try {
      await mongoMemoryServer.stop();
      console.log('✅ MongoDB Memory Server stopped');
    } catch (error) {
      console.warn('⚠️  Failed to stop MongoDB Memory Server:', error);
    }
  }

  console.log('🎉 Integration test cleanup complete!');
}, 30000);

/**
 * Wait for database services to be ready
 */
export const waitForServices = async (timeout: number = 30000): Promise<void> => {
  const start = Date.now();
  
  while (Date.now() - start < timeout) {
    try {
      // Check if QuestDB HTTP interface is ready
      const questdbUrl = `http://${process.env.QUESTDB_HOST}:${process.env.QUESTDB_HTTP_PORT}/status`;
      const response = await fetch(questdbUrl);
      
      if (response.ok) {
        console.log('✅ QuestDB is ready');
        return;
      }
    } catch (error) {
      // Service not ready yet, continue waiting
    }
    
    await new Promise(resolve => setTimeout(resolve, 1000));
  }
  
  throw new Error('Services did not become ready within timeout');
};

/**
 * Test utilities for integration tests
 */
export const integrationTestHelpers = {
  /**
   * Get QuestDB HTTP URL
   */
  getQuestDBUrl: () => `http://${process.env.QUESTDB_HOST}:${process.env.QUESTDB_HTTP_PORT}`,
  
  /**
   * Get PostgreSQL connection string
   */
  getPostgresUrl: () => 
    `postgresql://${process.env.POSTGRES_USER}:${process.env.POSTGRES_PASSWORD}@${process.env.POSTGRES_HOST}:${process.env.POSTGRES_PORT}/${process.env.POSTGRES_DB}`,
  
  /**
   * Get MongoDB connection string
   */
  getMongoUrl: () => {
    if (process.env.MONGODB_URI) {
      return process.env.MONGODB_URI;
    }
    return `mongodb://${process.env.MONGODB_USERNAME}:${process.env.MONGODB_PASSWORD}@${process.env.MONGODB_HOST}:${process.env.MONGODB_PORT}/${process.env.MONGODB_DATABASE}`;
  },
  
  /**
   * Wait for services to be ready
   */
  waitForServices
};
