/**
 * Test Environment Configuration Loader
 * 
 * This module handles loading and validating test environment variables
 * with type safety and default values.
 */

import * as dotenv from 'dotenv';
import * as path from 'path';
import { existsSync } from 'fs';

// Load test environment variables
export function loadTestEnvironment(): void {
  // CI Debug logging
  const isCI = process.env.CI === 'true';

  // First, load the main .env file (for integration tests that need real credentials)
  const mainEnvPath = path.resolve(process.cwd(), '.env');
  if (existsSync(mainEnvPath)) {
    dotenv.config({ path: mainEnvPath });
    if (isCI) {
      console.log('[CI-DEBUG] Loaded .env file from:', mainEnvPath);
    }
  }

  // Load base test environment
  const testEnvPath = path.resolve(process.cwd(), '.env.test');

  if (isCI) {
    console.log('[CI-DEBUG] Looking for .env.test at:', testEnvPath);
    console.log('[CI-DEBUG] File exists?', existsSync(testEnvPath));
  }

  if (existsSync(testEnvPath)) {
    // Don't override values from .env
    const result = dotenv.config({ path: testEnvPath, override: false });
    if (isCI && result.error) {
      console.error('[CI-DEBUG] Failed to load .env.test:', result.error);
    } else if (isCI && result.parsed) {
      console.log('[CI-DEBUG] Successfully loaded', Object.keys(result.parsed).length, 'env vars from .env.test');
    }
  } else if (isCI) {
    console.warn('[CI-DEBUG] .env.test file not found, will use defaults only');
  }

  // Load local test overrides (for sensitive values)
  const localEnvPath = path.resolve(process.cwd(), '.env.test.local');
  if (existsSync(localEnvPath)) {
    dotenv.config({ path: localEnvPath, override: true });
  }

  // Set test-specific defaults (only if not already set)
  setTestDefaults();

  // Validate required environment variables
  validateTestEnvironment();
}

/**
 * Set default values for test environment variables
 */
function setTestDefaults(): void {
  // Ensure we're in test mode
  process.env.NODE_ENV = 'test';
  process.env.TEST_ENVIRONMENT = 'true';
  
  // Set defaults if not already set
  const defaults: Record<string, string> = {
    // Database
    NODE_DB_PATH: ':memory:',
    REBUILD_ON_START: 'false',
    
    // API
    N8N_API_URL: 'http://localhost:3001/mock-api',
    N8N_API_KEY: 'test-api-key-12345',
    
    // Server
    PORT: '3001',
    HOST: '127.0.0.1',
    
    // Logging
    LOG_LEVEL: 'error',
    DEBUG: 'false',
    TEST_LOG_VERBOSE: 'false',
    
    // Timeouts
    TEST_TIMEOUT_UNIT: '5000',
    TEST_TIMEOUT_INTEGRATION: '15000',
    TEST_TIMEOUT_E2E: '30000',
    TEST_TIMEOUT_GLOBAL: '30000', // Reduced from 60s to 30s to catch hangs faster
    
    // Test execution
    TEST_RETRY_ATTEMPTS: '2',
    TEST_RETRY_DELAY: '1000',
    TEST_PARALLEL: 'true',
    TEST_MAX_WORKERS: '4',
    
    // Features
    FEATURE_MOCK_EXTERNAL_APIS: 'true',
    FEATURE_USE_TEST_CONTAINERS: 'false',
    MSW_ENABLED: 'true',
    MSW_API_DELAY: '0',
    
    // Paths
    TEST_FIXTURES_PATH: './tests/fixtures',
    TEST_DATA_PATH: './tests/data',
    TEST_SNAPSHOTS_PATH: './tests/__snapshots__',
    
    // Performance
    PERF_THRESHOLD_API_RESPONSE: '100',
    PERF_THRESHOLD_DB_QUERY: '50',
    PERF_THRESHOLD_NODE_PARSE: '200',
    
    // Caching
    CACHE_TTL: '0',
    CACHE_ENABLED: 'false',
    
    // Rate limiting
    RATE_LIMIT_MAX: '0',
    RATE_LIMIT_WINDOW: '0',
    
    // Error handling
    ERROR_SHOW_STACK: 'true',
    ERROR_SHOW_DETAILS: 'true',
    
    // Cleanup
    TEST_CLEANUP_ENABLED: 'true',
    TEST_CLEANUP_ON_FAILURE: 'false',
    
    // Database seeding
    TEST_SEED_DATABASE: 'true',
    TEST_SEED_TEMPLATES: 'true',
    
    // Network
    NETWORK_TIMEOUT: '5000',
    NETWORK_RETRY_COUNT: '0',
    
    // Memory
    TEST_MEMORY_LIMIT: '512',
    
    // Coverage
    COVERAGE_DIR: './coverage',
    COVERAGE_REPORTER: 'lcov,html,text-summary'
  };

  for (const [key, value] of Object.entries(defaults)) {
    if (!process.env[key]) {
      process.env[key] = value;
    }
  }
}

/**
 * Validate that required environment variables are set
 */
function validateTestEnvironment(): void {
  const required = [
    'NODE_ENV',
    'NODE_DB_PATH',
    'N8N_API_URL',
    'N8N_API_KEY'
  ];

  const missing = required.filter(key => !process.env[key]);
  
  if (missing.length > 0) {
    throw new Error(
      `Missing required test environment variables: ${missing.join(', ')}\n` +
      'Please ensure .env.test is properly configured.'
    );
  }

  // Validate NODE_ENV is set to test
  if (process.env.NODE_ENV !== 'test') {
    throw new Error(
      'NODE_ENV must be set to "test" when running tests.\n' +
      'This prevents accidental execution against production systems.'
    );
  }
}

/**
 * Get typed test environment configuration
 */
export function getTestConfig() {
  // Ensure defaults are set before accessing
  if (!process.env.N8N_API_URL) {
    setTestDefaults();
  }
  
  return {
    // Environment
    nodeEnv: process.env.NODE_ENV || 'test',
    isTest: process.env.TEST_ENVIRONMENT === 'true',
    
    // Database
    database: {
      path: process.env.NODE_DB_PATH || ':memory:',
      rebuildOnStart: process.env.REBUILD_ON_START === 'true',
      seedData: process.env.TEST_SEED_DATABASE === 'true',
      seedTemplates: process.env.TEST_SEED_TEMPLATES === 'true'
    },
    
    // API
    api: {
      url: process.env.N8N_API_URL || 'http://localhost:3001/mock-api',
      key: process.env.N8N_API_KEY || 'test-api-key-12345',
      webhookBaseUrl: process.env.N8N_WEBHOOK_BASE_URL,
      webhookTestUrl: process.env.N8N_WEBHOOK_TEST_URL
    },
    
    // Server
    server: {
      port: parseInt(process.env.PORT || '3001', 10),
      host: process.env.HOST || '127.0.0.1',
      corsOrigin: process.env.CORS_ORIGIN?.split(',') || []
    },
    
    // Authentication
    auth: {
      token: process.env.AUTH_TOKEN,
      mcpToken: process.env.MCP_AUTH_TOKEN
    },
    
    // Logging
    logging: {
      level: process.env.LOG_LEVEL || 'error',
      debug: process.env.DEBUG === 'true',
      verbose: process.env.TEST_LOG_VERBOSE === 'true',
      showStack: process.env.ERROR_SHOW_STACK === 'true',
      showDetails: process.env.ERROR_SHOW_DETAILS === 'true'
    },
    
    // Test execution
    execution: {
      timeouts: {
        unit: parseInt(process.env.TEST_TIMEOUT_UNIT || '5000', 10),
        integration: parseInt(process.env.TEST_TIMEOUT_INTEGRATION || '15000', 10),
        e2e: parseInt(process.env.TEST_TIMEOUT_E2E || '30000', 10),
        global: parseInt(process.env.TEST_TIMEOUT_GLOBAL || '60000', 10)
      },
      retry: {
        attempts: parseInt(process.env.TEST_RETRY_ATTEMPTS || '2', 10),
        delay: parseInt(process.env.TEST_RETRY_DELAY || '1000', 10)
      },
      parallel: process.env.TEST_PARALLEL === 'true',
      maxWorkers: parseInt(process.env.TEST_MAX_WORKERS || '4', 10)
    },
    
    // Features
    features: {
      coverage: process.env.FEATURE_TEST_COVERAGE === 'true',
      screenshots: process.env.FEATURE_TEST_SCREENSHOTS === 'true',
      videos: process.env.FEATURE_TEST_VIDEOS === 'true',
      trace: process.env.FEATURE_TEST_TRACE === 'true',
      mockExternalApis: process.env.FEATURE_MOCK_EXTERNAL_APIS === 'true',
      useTestContainers: process.env.FEATURE_USE_TEST_CONTAINERS === 'true'
    },
    
    // Mocking
    mocking: {
      msw: {
        enabled: process.env.MSW_ENABLED === 'true',
        apiDelay: parseInt(process.env.MSW_API_DELAY || '0', 10)
      },
      redis: {
        enabled: process.env.REDIS_MOCK_ENABLED === 'true',
        port: parseInt(process.env.REDIS_MOCK_PORT || '6380', 10)
      },
      elasticsearch: {
        enabled: process.env.ELASTICSEARCH_MOCK_ENABLED === 'true',
        port: parseInt(process.env.ELASTICSEARCH_MOCK_PORT || '9201', 10)
      }
    },
    
    // Paths
    paths: {
      fixtures: process.env.TEST_FIXTURES_PATH || './tests/fixtures',
      data: process.env.TEST_DATA_PATH || './tests/data',
      snapshots: process.env.TEST_SNAPSHOTS_PATH || './tests/__snapshots__'
    },
    
    // Performance
    performance: {
      thresholds: {
        apiResponse: parseInt(process.env.PERF_THRESHOLD_API_RESPONSE || '100', 10),
        dbQuery: parseInt(process.env.PERF_THRESHOLD_DB_QUERY || '50', 10),
        nodeParse: parseInt(process.env.PERF_THRESHOLD_NODE_PARSE || '200', 10)
      }
    },
    
    // Rate limiting
    rateLimiting: {
      max: parseInt(process.env.RATE_LIMIT_MAX || '0', 10),
      window: parseInt(process.env.RATE_LIMIT_WINDOW || '0', 10)
    },
    
    // Caching
    cache: {
      enabled: process.env.CACHE_ENABLED === 'true',
      ttl: parseInt(process.env.CACHE_TTL || '0', 10)
    },
    
    // Cleanup
    cleanup: {
      enabled: process.env.TEST_CLEANUP_ENABLED === 'true',
      onFailure: process.env.TEST_CLEANUP_ON_FAILURE === 'true'
    },
    
    // Network
    network: {
      timeout: parseInt(process.env.NETWORK_TIMEOUT || '5000', 10),
      retryCount: parseInt(process.env.NETWORK_RETRY_COUNT || '0', 10)
    },
    
    // Memory
    memory: {
      limit: parseInt(process.env.TEST_MEMORY_LIMIT || '512', 10)
    },
    
    // Coverage
    coverage: {
      dir: process.env.COVERAGE_DIR || './coverage',
      reporters: (process.env.COVERAGE_REPORTER || 'lcov,html,text-summary').split(',')
    }
  };
}

// Export type for the test configuration
export type TestConfig = ReturnType<typeof getTestConfig>;

/**
 * Helper to check if we're in test mode
 */
export function isTestMode(): boolean {
  return process.env.NODE_ENV === 'test' || process.env.TEST_ENVIRONMENT === 'true';
}

/**
 * Helper to get timeout for specific test type
 */
export function getTestTimeout(type: 'unit' | 'integration' | 'e2e' | 'global' = 'unit'): number {
  const config = getTestConfig();
  return config.execution.timeouts[type];
}

/**
 * Helper to check if a feature is enabled
 */
export function isFeatureEnabled(feature: keyof TestConfig['features']): boolean {
  const config = getTestConfig();
  return config.features[feature];
}

/**
 * Reset environment to defaults (useful for test isolation)
 */
export function resetTestEnvironment(): void {
  // Clear all test-specific environment variables
  const testKeys = Object.keys(process.env).filter(key => 
    key.startsWith('TEST_') || 
    key.startsWith('FEATURE_') ||
    key.startsWith('MSW_') ||
    key.startsWith('PERF_')
  );
  
  testKeys.forEach(key => {
    delete process.env[key];
  });
  
  // Reload defaults
  loadTestEnvironment();
}