/**
 * 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';

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

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;

  constructor(onProgress?: (result: TestResult) => void) {
    this.onProgress = onProgress;
  }

  /**
   * 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 (const test of suite.tests) {
      const result = await this.runTest(test, suite.name);
      suiteResults.push(result);
      
      if (this.shouldStop) {
        break;
      }
    }
    
    return suiteResults;
  }

  /**
   * Run all suites sequentially
   */
  async runAllSuites(suites: TestSuite[]): Promise<TestResult[]> {
    this.results = [];
    this.isRunning = true;
    this.shouldStop = false;
    
    for (const suite of suites) {
      await this.runSuite(suite);
      
      if (this.shouldStop) {
        break;
      }
    }
    
    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;
  } {
    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 };
  }

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

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