/**
 * Integration Test Runner
 * 
 * Core infrastructure for running comprehensive integration tests.
 * Tests run against real database functions, edge functions, and API endpoints.
 */

import { moderationTestSuite } from './suites/moderationTests';
import { moderationLockTestSuite } from './suites/moderationLockTests';
import { moderationDependencyTestSuite } from './suites/moderationDependencyTests';
import { approvalPipelineTestSuite } from './suites/approvalPipelineTests';
import { cleanupTestData, type CleanupSummary } from './testCleanup';

/**
 * Registry of all available test suites
 */
export const ALL_TEST_SUITES = [
  moderationTestSuite,
  moderationLockTestSuite,
  moderationDependencyTestSuite,
  approvalPipelineTestSuite,
];

export interface TestResult {
  id: string;
  name: string;
  suite: string;
  status: 'pass' | 'fail' | 'skip' | 'running';
  duration: number; // milliseconds
  error?: string;
  details?: any;
  timestamp: string;
  stack?: string;
}

export interface Test {
  id: string;
  name: string;
  description: string;
  run: () => Promise<TestResult>;
}

export interface TestSuite {
  id: string;
  name: string;
  description: string;
  tests: Test[];
}

export class IntegrationTestRunner {
  private results: TestResult[] = [];
  private isRunning = false;
  private shouldStop = false;
  private onProgress?: (result: TestResult) => void;
  private delayBetweenTests: number;
  private cleanupEnabled: boolean;
  private cleanupSummary?: CleanupSummary;

  constructor(
    onProgress?: (result: TestResult) => void, 
    delayBetweenTests: number = 8000,
    cleanupEnabled: boolean = true
  ) {
    this.onProgress = onProgress;
    this.delayBetweenTests = delayBetweenTests; // Default 8 seconds to prevent rate limiting
    this.cleanupEnabled = cleanupEnabled;
  }

  /**
   * Wait for specified milliseconds (for rate limiting prevention)
   */
  private async delay(ms: number): Promise<void> {
    return new Promise(resolve => setTimeout(resolve, ms));
  }

  /**
   * Run a single test with error handling
   */
  async runTest(test: Test, suiteName: string): Promise<TestResult> {
    if (this.shouldStop) {
      return {
        id: test.id,
        name: test.name,
        suite: suiteName,
        status: 'skip',
        duration: 0,
        timestamp: new Date().toISOString(),
        details: { reason: 'Test run stopped by user' }
      };
    }

    // Mark as running
    const runningResult: TestResult = {
      id: test.id,
      name: test.name,
      suite: suiteName,
      status: 'running',
      duration: 0,
      timestamp: new Date().toISOString(),
    };
    
    if (this.onProgress) {
      this.onProgress(runningResult);
    }

    try {
      const result = await test.run();
      this.results.push(result);
      
      if (this.onProgress) {
        this.onProgress(result);
      }
      
      return result;
    } catch (error) {
      const failResult: TestResult = {
        id: test.id,
        name: test.name,
        suite: suiteName,
        status: 'fail',
        duration: 0,
        error: error instanceof Error ? error.message : String(error),
        stack: error instanceof Error ? error.stack : undefined,
        timestamp: new Date().toISOString(),
      };
      
      this.results.push(failResult);
      
      if (this.onProgress) {
        this.onProgress(failResult);
      }
      
      return failResult;
    }
  }

  /**
   * Run all tests in a suite
   */
  async runSuite(suite: TestSuite): Promise<TestResult[]> {
    const suiteResults: TestResult[] = [];
    
    for (let i = 0; i < suite.tests.length; i++) {
      const test = suite.tests[i];
      const result = await this.runTest(test, suite.name);
      suiteResults.push(result);
      
      if (this.shouldStop) {
        break;
      }

      // Add delay between tests to prevent rate limiting (except after the last test)
      if (i < suite.tests.length - 1 && this.delayBetweenTests > 0) {
        // Report delay status with countdown
        const delaySeconds = this.delayBetweenTests / 1000;
        const delayResult: TestResult = {
          id: `delay-${Date.now()}`,
          name: `⏳ Rate limit delay: ${delaySeconds}s`,
          suite: suite.name,
          status: 'running',
          duration: 0,
          timestamp: new Date().toISOString(),
          details: { 
            reason: 'Pausing to prevent rate limiting',
            delayMs: this.delayBetweenTests
          }
        };
        
        if (this.onProgress) {
          this.onProgress(delayResult);
        }

        await this.delay(this.delayBetweenTests);

        // Mark delay as complete
        const delayCompleteResult: TestResult = {
          ...delayResult,
          status: 'skip',
          duration: this.delayBetweenTests,
          details: { reason: 'Rate limit delay completed' }
        };
        
        if (this.onProgress) {
          this.onProgress(delayCompleteResult);
        }
      }
    }
    
    return suiteResults;
  }

  /**
   * Run all suites sequentially
   */
  async runAllSuites(suites: TestSuite[]): Promise<TestResult[]> {
    this.results = [];
    this.isRunning = true;
    this.shouldStop = false;
    
    // Track submission-heavy suites for adaptive delays
    const submissionHeavySuites = [
      'Entity Submission & Validation',
      'Approval Pipeline',
      'Unit Conversion Tests',
      'Performance & Scalability'
    ];
    
    for (let i = 0; i < suites.length; i++) {
      const isHeavySuite = submissionHeavySuites.includes(suites[i].name);
      
      // PREEMPTIVE delay BEFORE heavy suites start (prevents rate limit buildup)
      if (isHeavySuite && i > 0) {
        const preemptiveDelayMs = 8000; // 8s "cooldown" before heavy suite
        const delaySeconds = preemptiveDelayMs / 1000;
        const delayResult: TestResult = {
          id: `preemptive-delay-${Date.now()}`,
          name: `⏳ Pre-suite cooldown: ${delaySeconds}s (preparing for ${suites[i].name})`,
          suite: 'System',
          status: 'running',
          duration: 0,
          timestamp: new Date().toISOString(),
          details: { 
            reason: 'Preemptive rate limit prevention before submission-heavy suite',
            nextSuite: suites[i].name
          }
        };
        
        if (this.onProgress) {
          this.onProgress(delayResult);
        }
        
        await this.delay(preemptiveDelayMs);
        
        if (this.onProgress) {
          this.onProgress({ 
            ...delayResult, 
            status: 'skip', 
            duration: preemptiveDelayMs,
            details: { reason: 'Cooldown completed' }
          });
        }
      }
      
      await this.runSuite(suites[i]);
      
      if (this.shouldStop) {
        break;
      }
      
      // REACTIVE delay AFTER suites complete
      if (i < suites.length - 1 && this.delayBetweenTests > 0) {
        // Longer delay after submission-heavy suites
        const delayMs = isHeavySuite 
          ? this.delayBetweenTests * 2.25  // 18s delay after heavy suites (increased from 12s)
          : this.delayBetweenTests;         // 8s delay after others (increased from 6s)
        
        const delaySeconds = delayMs / 1000;
        const delayResult: TestResult = {
          id: `suite-delay-${Date.now()}`,
          name: `⏳ Suite completion delay: ${delaySeconds}s${isHeavySuite ? ' (submission-heavy)' : ''}`,
          suite: 'System',
          status: 'running',
          duration: 0,
          timestamp: new Date().toISOString(),
          details: { 
            reason: 'Pausing between suites to prevent rate limiting',
            isSubmissionHeavy: isHeavySuite
          }
        };
        
        if (this.onProgress) {
          this.onProgress(delayResult);
        }
        
        await this.delay(delayMs);
        
        if (this.onProgress) {
          this.onProgress({ 
            ...delayResult, 
            status: 'skip', 
            duration: delayMs,
            details: { reason: 'Suite delay completed' }
          });
        }
      }
    }
    
    // Run cleanup after all tests complete (if enabled)
    if (this.cleanupEnabled && !this.shouldStop) {
      const cleanupStartResult: TestResult = {
        id: `cleanup-start-${Date.now()}`,
        name: '🧹 Starting test data cleanup...',
        suite: 'System',
        status: 'running',
        duration: 0,
        timestamp: new Date().toISOString(),
        details: { reason: 'Removing test fixtures to prevent database bloat' }
      };
      
      if (this.onProgress) {
        this.onProgress(cleanupStartResult);
      }
      
      try {
        this.cleanupSummary = await cleanupTestData();
        
        const cleanupCompleteResult: TestResult = {
          id: `cleanup-complete-${Date.now()}`,
          name: `✅ Cleanup complete: ${this.cleanupSummary.totalDeleted} records deleted`,
          suite: 'System',
          status: this.cleanupSummary.success ? 'pass' : 'fail',
          duration: this.cleanupSummary.totalDuration,
          timestamp: new Date().toISOString(),
          details: {
            totalDeleted: this.cleanupSummary.totalDeleted,
            results: this.cleanupSummary.results,
            success: this.cleanupSummary.success
          }
        };
        
        if (this.onProgress) {
          this.onProgress(cleanupCompleteResult);
        }
      } catch (error) {
        const cleanupErrorResult: TestResult = {
          id: `cleanup-error-${Date.now()}`,
          name: '❌ Cleanup failed',
          suite: 'System',
          status: 'fail',
          duration: 0,
          timestamp: new Date().toISOString(),
          error: error instanceof Error ? error.message : String(error)
        };
        
        if (this.onProgress) {
          this.onProgress(cleanupErrorResult);
        }
      }
    }
    
    this.isRunning = false;
    return this.results;
  }

  /**
   * Stop the test run
   */
  stop(): void {
    this.shouldStop = true;
  }

  /**
   * Get all results
   */
  getResults(): TestResult[] {
    return this.results;
  }

  /**
   * Get summary statistics
   */
  getSummary(): {
    total: number;
    passed: number;
    failed: number;
    skipped: number;
    running: number;
    totalDuration: number;
    cleanup?: CleanupSummary;
  } {
    const total = this.results.length;
    const passed = this.results.filter(r => r.status === 'pass').length;
    const failed = this.results.filter(r => r.status === 'fail').length;
    const skipped = this.results.filter(r => r.status === 'skip').length;
    const running = this.results.filter(r => r.status === 'running').length;
    const totalDuration = this.results.reduce((sum, r) => sum + r.duration, 0);
    
    return { 
      total, 
      passed, 
      failed, 
      skipped, 
      running, 
      totalDuration,
      cleanup: this.cleanupSummary
    };
  }

  /**
   * Check if runner is currently running
   */
  getIsRunning(): boolean {
    return this.isRunning;
  }

  /**
   * Reset runner state
   */
  reset(): void {
    this.results = [];
    this.isRunning = false;
    this.shouldStop = false;
    this.cleanupSummary = undefined;
  }

  /**
   * Get cleanup summary
   */
  getCleanupSummary(): CleanupSummary | undefined {
    return this.cleanupSummary;
  }

  /**
   * Enable or disable automatic cleanup
   */
  setCleanupEnabled(enabled: boolean): void {
    this.cleanupEnabled = enabled;
  }
}
