import type { 
  IInitializeRunnerUseCase,
  IDefineSessionUseCase,
  IRunSingleSessionUseCase,
  IRunAllSessionsUseCase,
  IClearAllSessionsUseCase,
  IDestroyRunnerUseCase
} from '../../interfaces/use-cases/runner-use-cases';
import type { MultiSessionConfig, SessionResult, SessionMeta } from '../../interfaces/core/types';
import type { BatchExecutionOptions } from '../../interfaces/core/batch-execution';
import type { ISessionMetaRepository } from '../../interfaces/repositories/session-meta-repository';
import type { IReportRepository } from '../../interfaces/repositories/report-repository';
import type { IUnifiedEventBus } from '../../interfaces/events/unified-event-bus';
import type { IMochaIntegration } from '../../interfaces/infrastructure/mocha-integration';

// Use the global interface from mocha-integration.ts
declare global {
  interface Window {
    MochaMultipleSessions?: {
      testSessionSetup: (config: any) => void;
      onAnySessionEvent: (callback: (event: any) => void) => () => void;
      testSession: (label: string, setupFn: () => void, options?: any) => Promise<string>;
      runSession: (label: string) => Promise<any>;
      runAllSessions: () => Promise<any[]>;
    };
  }
}

export class InitializeRunnerUseCase implements IInitializeRunnerUseCase {
  constructor(
    private mochaIntegration: IMochaIntegration,
    private eventBus: IUnifiedEventBus
  ) {}

  execute(config?: Partial<MultiSessionConfig>): void {
    const finalConfig: MultiSessionConfig = {
      windowVariablePrefix: 'mochaSession__',
      autoInit: true,
      captureConsoleLog: true,
      sourceCode: true,
      attachStats: true,
      ...config
    };

    try {
      this.mochaIntegration.setupTestEnvironment(finalConfig);
      
      this.eventBus.emitRunnerEvent({
        type: 'runner:initialized',
        timestamp: new Date(),
        data: {
          configUsed: finalConfig,
          sessionCount: 0,
          timestamp: new Date()
        }
      });
      
    } catch (error) {
      this.eventBus.emitRunnerEvent({
        type: 'runner:config-updated',
        timestamp: new Date(),
        data: {
          oldConfig: {},
          newConfig: finalConfig,
          changes: ['initialization_failed']
        }
      });
      throw error;
    }
  }
}

export class DefineSessionUseCase implements IDefineSessionUseCase {
  constructor(
    private sessionRepository: ISessionMetaRepository,
    private reportRepository: IReportRepository,
    private eventBus: IUnifiedEventBus
  ) {}

  execute(label: string, setupFn: () => void): void {
    const resultKey = this.reportRepository.generateResultKey(label);
    
    const sessionMeta: SessionMeta = {
      label,
      resultKey,
      setupFn,
      status: 'ready',
      timestamp: new Date()
    };

    this.sessionRepository.save(sessionMeta);
    
    this.eventBus.emitRunnerEvent({
      type: 'runner:session-defined',
      timestamp: new Date(),
      data: {
        sessionLabel: label,
        sessionMeta,
        totalSessions: this.sessionRepository.findAll().length
      }
    });
  }
}

export class RunSingleSessionUseCase implements IRunSingleSessionUseCase {
  constructor(
    private sessionRepository: ISessionMetaRepository,
    private reportRepository: IReportRepository,
    private eventBus: IUnifiedEventBus
  ) {}

  async execute(label: string): Promise<SessionResult> {
    const sessionMeta = this.sessionRepository.findByLabel(label);
    if (!sessionMeta) {
      throw new Error(`Session '${label}' not found`);
    }

    try {
      // Mark as running
      this.sessionRepository.markAsRunning(label);
      
      // Use the existing mocha-multiple-sessions library
      if (!window.MochaMultipleSessions) {
        throw new Error('MochaMultipleSessions library not available');
      }

      // Create the session first with better error handling
      try {
        await window.MochaMultipleSessions.testSession(label, sessionMeta.setupFn);
        console.log(`Session '${label}' created successfully`);
      } catch (sessionCreationError) {
        console.error(`Failed to create session '${label}':`, sessionCreationError);
        throw new Error(`Session creation failed for '${label}': ${sessionCreationError}`);
      }
      
      // Run the session with better error handling
      let result;
      try {
        result = await window.MochaMultipleSessions.runSession(label);
        console.log(`Session '${label}' execution completed:`, result);
      } catch (sessionRunError) {
        console.error(`Failed to run session '${label}':`, sessionRunError);
        throw new Error(`Session execution failed for '${label}': ${sessionRunError}`);
      }
      
      // Wait for detailed report to be available
      try {
        await this.reportRepository.waitForReport(sessionMeta.resultKey, 5000);
        console.log(`Detailed report available for '${label}'`);
      } catch (waitError) {
        console.warn(`Timeout waiting for detailed report for ${label}:`, waitError);
        // Don't throw here, continue with basic result
      }

      // Mark as completed
      this.sessionRepository.markAsCompleted(label, result);

      const sessionResult: SessionResult = {
        label,
        status: result.success ? 'passed' : 'failed',
        success: result.success,
        timestamp: new Date(),
        duration: result.duration,
        stats: result.stats
      };

      console.log(`Session '${label}' result:`, sessionResult);
      return sessionResult;

    } catch (error) {
      console.error(`Error in session '${label}':`, error);
      this.sessionRepository.markAsFailed(label, error as Error);
      
      const sessionResult: SessionResult = {
        label,
        status: 'failed',
        success: false,
        timestamp: new Date(),
        error: error as Error
      };

      return sessionResult;
    }
  }
}

export class RunAllSessionsUseCase implements IRunAllSessionsUseCase {
  constructor(
    private sessionRepository: ISessionMetaRepository,
    private runSingleSessionUseCase: IRunSingleSessionUseCase,
    private eventBus: IUnifiedEventBus
  ) {}

  async execute(options?: BatchExecutionOptions): Promise<Record<string, SessionResult>> {
    const allSessions = this.sessionRepository.findAll();
    
    if (allSessions.length === 0) {
      throw new Error('No sessions defined');
    }

    const sessionLabels = allSessions.map(s => s.label);
    const startTime = Date.now();

    // Emit batch start event
    this.eventBus.emitRunnerEvent({
      type: 'runner:batch-started',
      timestamp: new Date(),
      data: {
        sessionLabels,
        totalSessions: sessionLabels.length,
        executionMode: options?.mode || 'sequential',
        options
      }
    });

    const results: Record<string, SessionResult> = {};
    let completed = 0;

    try {
      if (options?.mode === 'parallel') {
        // Run in parallel
        const promises = sessionLabels.map(async (label: string) => {
          const result = await this.runSingleSessionUseCase.execute(label);
          results[label] = result;
          completed++;
          
          this.eventBus.emitRunnerEvent({
            type: 'runner:batch-progress',
            timestamp: new Date(),
            data: {
              completed,
              total: sessionLabels.length,
              currentSession: label,
              completedSessions: Object.keys(results),
              failedSessions: Object.keys(results).filter((l: string) => !results[l]?.success),
              progress: Math.round((completed / sessionLabels.length) * 100)
            }
          });
          
          return result;
        });

        await Promise.all(promises);
      } else {
        // Run sequentially
        for (const label of sessionLabels) {
          if (options?.stopOnFirstFailure && completed > 0) {
            const hasFailures = Object.values(results).some((r: SessionResult) => !r.success);
            if (hasFailures) break;
          }

          const result = await this.runSingleSessionUseCase.execute(label);
          results[label] = result;
          completed++;

          this.eventBus.emitRunnerEvent({
            type: 'runner:batch-progress',
            timestamp: new Date(),
            data: {
              completed,
              total: sessionLabels.length,
              currentSession: label,
              completedSessions: Object.keys(results),
              failedSessions: Object.keys(results).filter((l: string) => !results[l]?.success),
              progress: Math.round((completed / sessionLabels.length) * 100)
            }
          });
        }
      }

      const duration = Date.now() - startTime;
      const successfulSessions = Object.values(results).filter(r => r.success).length;
      const failedSessions = Object.values(results).filter(r => !r.success).length;

      // Emit completion event
      this.eventBus.emitRunnerEvent({
        type: 'runner:batch-completed',
        timestamp: new Date(),
        data: {
          totalSessions: sessionLabels.length,
          successfulSessions,
          failedSessions,
          duration,
          sessionResults: results,
          overallSuccess: failedSessions === 0
        }
      });

      return results;

    } catch (error) {
      // Emit failure event
      this.eventBus.emitRunnerEvent({
        type: 'runner:batch-failed',
        timestamp: new Date(),
        data: {
          error: error as Error,
          completedSessions: Object.keys(results),
          partialResults: results
        }
      });

      throw error;
    }
  }
}

export class ClearAllSessionsUseCase implements IClearAllSessionsUseCase {
  constructor(
    private sessionRepository: ISessionMetaRepository,
    private reportRepository: IReportRepository,
    private eventBus: IUnifiedEventBus
  ) {}

  async execute(): Promise<void> {
    const allSessions = this.sessionRepository.findAll();
    const clearedSessions = allSessions.map(s => s.label);
    const clearedReports: string[] = [];

    // Clear all reports
    for (const session of allSessions) {
      if (this.reportRepository.hasReport(session.resultKey)) {
        this.reportRepository.clearReport(session.resultKey);
        clearedReports.push(session.resultKey);
      }
    }

    // Clear session repository
    this.sessionRepository.clear();

    // Emit cleared event
    this.eventBus.emitRunnerEvent({
      type: 'runner:cleared',
      timestamp: new Date(),
      data: {
        clearedSessions,
        clearedReports
      }
    });
  }
}

export class DestroyRunnerUseCase implements IDestroyRunnerUseCase {
  constructor(
    private clearAllSessionsUseCase: IClearAllSessionsUseCase,
    private eventBus: IUnifiedEventBus
  ) {}

  async execute(): Promise<void> {
    // Clear all sessions first
    await this.clearAllSessionsUseCase.execute();

    // Emit destroy event
    this.eventBus.emitRunnerEvent({
      type: 'runner:destroyed',
      timestamp: new Date()
    });

    // Destroy event bus
    this.eventBus.destroy();
  }
}
